Objective c 如何以编程方式将UIScrollView移动到键盘上方控件中的焦点?
我的Objective c 如何以编程方式将UIScrollView移动到键盘上方控件中的焦点?,objective-c,iphone,user-interface,Objective C,Iphone,User Interface,我的UIScrollView上有6个UITextFields。现在,我可以按用户请求滚动。但当键盘出现时,一些文本字段被隐藏 这不是用户友好的 如何以编程方式滚动视图,以确保键盘不会隐藏文本字段?如果将文本字段的委托设置为程序中的控制器对象,则可以让该对象实现textfieldDiBeginEdit:和textFieldShouldReturn:方法。第一种方法可用于滚动到文本字段,第二种方法可用于向后滚动 您可以在我的博客中找到我用于此目的的代码:。我没有在UIScrollView中测试此代码
UIScrollView
上有6个UITextFields
。现在,我可以按用户请求滚动。但当键盘出现时,一些文本字段被隐藏
这不是用户友好的
如何以编程方式滚动视图,以确保键盘不会隐藏文本字段?如果将文本字段的
委托设置为程序中的控制器对象,则可以让该对象实现textfieldDiBeginEdit:
和textFieldShouldReturn:
方法。第一种方法可用于滚动到文本字段,第二种方法可用于向后滚动
您可以在我的博客中找到我用于此目的的代码:。我没有在UIScrollView
中测试此代码的文本视图,但它应该可以工作。最后,一个简单的修复:
UIScrollView* v = (UIScrollView*) self.view ;
CGRect rc = [textField bounds];
rc = [textField convertRect:rc toView:v];
rc.origin.x = 0 ;
rc.origin.y -= 60 ;
rc.size.height = 400;
[self.scroll scrollRectToVisible:rc animated:YES];
现在我想只有结合上面的链接,这是设置 以下是对我有用的东西。在为键盘调整视图之前,使用实例变量保存UIScrollView的偏移值,以便在UITextField返回后恢复以前的状态:
//header
@interface TheViewController : UIViewController <UITextFieldDelegate> {
CGPoint svos;
}
//implementation
- (void)textFieldDidBeginEditing:(UITextField *)textField {
svos = scrollView.contentOffset;
CGPoint pt;
CGRect rc = [textField bounds];
rc = [textField convertRect:rc toView:scrollView];
pt = rc.origin;
pt.x = 0;
pt.y -= 60;
[scrollView setContentOffset:pt animated:YES];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[scrollView setContentOffset:svos animated:YES];
[textField resignFirstResponder];
return YES;
}
//头
@视图控制器接口:UIViewController{
CGPoint-svos;
}
//实施
-(无效)textFieldDidBeginEditing:(UITextField*)textField{
svos=scrollView.contentOffset;
cgpt;
CGRect rc=[textField界限];
rc=[textField convertRect:rc-toView:scrollView];
pt=rc.原点;
pt.x=0;
pt.y-=60;
[scrollView setContentOffset:pt动画:是];
}
-(BOOL)textField应返回:(UITextField*)textField{
[scrollView setContentOffset:svos动画:是];
[textField resignFirstResponder];
返回YES;
}
我已经创建了一个通用的嵌入式UIScrollView和UITableView子类,负责将其中的所有文本字段移出键盘
当键盘即将出现时,子类将找到将要编辑的子视图,并调整其帧和内容偏移以确保视图可见,并使用与键盘弹出窗口匹配的动画。当键盘消失时,它会恢复原来的大小
它基本上适用于任何设置,无论是基于UITableView的界面,还是由手动放置的视图组成的界面
是的
到目前为止发布的答案对我不起作用,因为我有一个相当深的UIView嵌套结构。此外,我还有一个问题,就是其中一些答案只适用于特定的设备方向
这是我的解决方案,希望能让你在这件事上少浪费一些时间
我的UIViewTextView源于UIView,是一个UITextView委托,在从该UITextView的XML文件中读取了一些参数后,添加了一个UITextView(为了清晰起见,此处省略了该XML部分)
以下是专用接口定义:
#import "UIViewTextView.h"
#import <CoreGraphics/CoreGraphics.h>
#import <CoreGraphics/CGColor.h>
@interface UIViewTextView (/**/) {
@private
UITextView *tf;
/*
* Current content scroll view
* position and frame
*/
CGFloat currentScrollViewPosition;
CGFloat currentScrollViewHeight;
CGFloat kbHeight;
CGFloat kbTop;
/*
* contentScrollView is the UIScrollView
* that contains ourselves.
*/
UIScrollView contentScrollView;
}
@end
完成后,我们需要处理键盘显示事件。在调用TextViewBeginEdit之前调用它,因此我们可以使用它来查找键盘的一些属性。本质上,我们想知道键盘的高度。不幸的是,这需要从横向模式下的宽度属性中获取:
-(void)keyboardWasShown:(NSNotification*)aNotification {
NSDictionary* info = [aNotification userInfo];
CGRect kbRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGSize kbSize = kbRect.size;
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat sWidth = screenRect.size.width;
CGFloat sHeight = screenRect.size.height;
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if ((orientation == UIDeviceOrientationPortrait)
||(orientation == UIDeviceOrientationPortraitUpsideDown)) {
kbHeight = kbSize.height;
kbTop = sHeight - kbHeight;
} else {
//Note that the keyboard size is not oriented
//so use width property instead
kbHeight = kbSize.width;
kbTop = sWidth - kbHeight;
}
接下来,我们需要在开始编辑时实际滚动。我们在这里这样做:
- (void) textViewDidBeginEditing:(UITextView *)textView {
/*
* Memorize the current scroll position
*/
currentScrollViewPosition = contentScrollView.contentOffset.y;
/*
* Memorize the current scroll view height
*/
currentScrollViewHeight = contentScrollView.frame.size.height;
// My top position
CGFloat myTop = [self convertPoint:self.bounds.origin toView:[UIApplication sharedApplication].keyWindow.rootViewController.view].y;
// My height
CGFloat myHeight = self.frame.size.height;
// My bottom
CGFloat myBottom = myTop + myHeight;
// Eventual overlap
CGFloat overlap = myBottom - kbTop;
/*
* If there's no overlap, there's nothing to do.
*/
if (overlap < 0) {
return;
}
/*
* Calculate the new height
*/
CGRect crect = contentScrollView.frame;
CGRect nrect = CGRectMake(crect.origin.x, crect.origin.y, crect.size.width, currentScrollViewHeight + overlap);
/*
* Set the new height
*/
[contentScrollView setFrame:nrect];
/*
* Set the new scroll position
*/
CGPoint npos;
npos.x = contentScrollView.contentOffset.x;
npos.y = contentScrollView.contentOffset.y + overlap;
[contentScrollView setContentOffset:npos animated:NO];
}
键盘上没有什么可做的了,隐藏了事件处理程序;不管怎样,我们都会把它放在里面:
-(void)keyboardWasHidden:(NSNotification*)aNotification {
}
就这样
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
@end
我知道这已经很老了,但上面的解决方案中仍然没有一个具备“完美”无bug、向后兼容和无闪烁动画所需的所有高级定位功能。
让我分享一下我的解决方案(假设您已经设置了UIKeyboardWill(Show | Hide)通知
):
//在发送UIKeyboardWillShowNotification时调用
-(无效)键盘将显示:(NSNotification*)通知
{
//如果我们没有视图或在任何窗口中都看不到,我们不在乎
如果(!self.isViewLoaded | |!self.view.window){
返回;
}
NSDictionary*userInfo=[notification userInfo];
CGRect键盘框架窗口;
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey]getValue:&keyboardFrameInWindow];
//键盘框是在窗口级坐标中指定的。这将计算框,就像它是我们视图的子视图一样,使其成为滚动视图的同级视图
CGRect keyboardFrameInView=[self.view convertRect:keyboardFrameInWindow fromView:nil];
CGRect scrollView KEYBOARDCROSSION=cGRECTCROSION(_scrollView.frame,keyboardFrameInView);
UIEdgeInsets newContentInsets=UIEdgeInsetsMake(0,0,scrollViewKeyboardIntersection.size.height,0);
//这是一种旧的动画方法,但是唯一一种在参数(持续时间、曲线)和userInfo字典中包含的值之间保持一致性的方法。
[UIView beginAnimations:nil上下文:NULL];
[UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey]doubleValue];
[UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfo]intValue];
_scrollView.contentInset=newContentInsets;
_scrollView.ScrollIndicationSets=新内容插入集;
/*
*取决于视觉布局,\ u focusedControl应该是输入字段(UITextField,…)或其他元素
*应可见,例如金额文本字段下方的购买按钮
*在委托中设置_focusedControl是有意义的,例如-textFieldShouldBeginEditing:如果您有多个输入字段
*/
如果(_focusedControl){
CGRect controlFrameInScrollView=[\u scrollView convertRect:\u focusedControl.bounds fromView:\u focusedControl];//如果控件在滚动视图下面的层次结构中较深,这将计算帧,就像它是直接子视图一样
controlFrameInScrollView=CGRectInset(controlFrameInScrollView,0,-10);//用任何漂亮的视觉偏移替换10
- (void) textViewDidEndEditing:(UITextView *)textView {
/*
* Reset the scroll view position
*/
CGRect crect = contentScrollView.frame;
CGRect nrect = CGRectMake(crect.origin.x, crect.origin.y, crect.size.width, currentScrollViewHeight);
[contentScrollView setFrame:nrect];
/*
* Reset the scroll view height
*/
CGPoint npos;
npos.x = contentScrollView.contentOffset.x;
npos.y = currentScrollViewPosition;
[contentScrollView setContentOffset:npos animated:YES];
[tf resignFirstResponder];
// ... do something with your data ...
}
-(void)keyboardWasHidden:(NSNotification*)aNotification {
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
@end
// Called when UIKeyboardWillShowNotification is sent
- (void)keyboardWillShow:(NSNotification*)notification
{
// if we have no view or are not visible in any window, we don't care
if (!self.isViewLoaded || !self.view.window) {
return;
}
NSDictionary *userInfo = [notification userInfo];
CGRect keyboardFrameInWindow;
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindow];
// the keyboard frame is specified in window-level coordinates. this calculates the frame as if it were a subview of our view, making it a sibling of the scroll view
CGRect keyboardFrameInView = [self.view convertRect:keyboardFrameInWindow fromView:nil];
CGRect scrollViewKeyboardIntersection = CGRectIntersection(_scrollView.frame, keyboardFrameInView);
UIEdgeInsets newContentInsets = UIEdgeInsetsMake(0, 0, scrollViewKeyboardIntersection.size.height, 0);
// this is an old animation method, but the only one that retains compaitiblity between parameters (duration, curve) and the values contained in the userInfo-Dictionary.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
_scrollView.contentInset = newContentInsets;
_scrollView.scrollIndicatorInsets = newContentInsets;
/*
* Depending on visual layout, _focusedControl should either be the input field (UITextField,..) or another element
* that should be visible, e.g. a purchase button below an amount text field
* it makes sense to set _focusedControl in delegates like -textFieldShouldBeginEditing: if you have multiple input fields
*/
if (_focusedControl) {
CGRect controlFrameInScrollView = [_scrollView convertRect:_focusedControl.bounds fromView:_focusedControl]; // if the control is a deep in the hierarchy below the scroll view, this will calculate the frame as if it were a direct subview
controlFrameInScrollView = CGRectInset(controlFrameInScrollView, 0, -10); // replace 10 with any nice visual offset between control and keyboard or control and top of the scroll view.
CGFloat controlVisualOffsetToTopOfScrollview = controlFrameInScrollView.origin.y - _scrollView.contentOffset.y;
CGFloat controlVisualBottom = controlVisualOffsetToTopOfScrollview + controlFrameInScrollView.size.height;
// this is the visible part of the scroll view that is not hidden by the keyboard
CGFloat scrollViewVisibleHeight = _scrollView.frame.size.height - scrollViewKeyboardIntersection.size.height;
if (controlVisualBottom > scrollViewVisibleHeight) { // check if the keyboard will hide the control in question
// scroll up until the control is in place
CGPoint newContentOffset = _scrollView.contentOffset;
newContentOffset.y += (controlVisualBottom - scrollViewVisibleHeight);
// make sure we don't set an impossible offset caused by the "nice visual offset"
// if a control is at the bottom of the scroll view, it will end up just above the keyboard to eliminate scrolling inconsistencies
newContentOffset.y = MIN(newContentOffset.y, _scrollView.contentSize.height - scrollViewVisibleHeight);
[_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
} else if (controlFrameInScrollView.origin.y < _scrollView.contentOffset.y) {
// if the control is not fully visible, make it so (useful if the user taps on a partially visible input field
CGPoint newContentOffset = _scrollView.contentOffset;
newContentOffset.y = controlFrameInScrollView.origin.y;
[_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
}
}
[UIView commitAnimations];
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillHide:(NSNotification*)notification
{
// if we have no view or are not visible in any window, we don't care
if (!self.isViewLoaded || !self.view.window) {
return;
}
NSDictionary *userInfo = notification.userInfo;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:[[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[[userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
// undo all that keyboardWillShow-magic
// the scroll view will adjust its contentOffset apropriately
_scrollView.contentInset = UIEdgeInsetsZero;
_scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;
[UIView commitAnimations];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
// self.scrlViewUI.contentOffset = CGPointMake(0, textField.frame.origin.y);
[_scrlViewUI setContentOffset:CGPointMake(0,textField.center.y-90) animated:YES];
tes=YES;
[self viewDidLayoutSubviews];
}
CGPoint bottomOffset = CGPointMake(0, self.MainScrollView.contentSize.height - self.MainScrollView.bounds.size.height);
[self.MainScrollView setContentOffset:bottomOffset animated:YES];
[self.MainScrollView scrollRectToVisible:CGRectMake(0, self.MainScrollView.contentSize.height - self.MainScrollView.bounds.size.height-30, MainScrollView.frame.size.width, MainScrollView.frame.size.height) animated:YES];
CGPoint contentOffset;
bool isScroll;
- (void)textFieldDidBeginEditing:(UITextField *)textField {
contentOffset = self.myScroll.contentOffset;
CGPoint newOffset;
newOffset.x = contentOffset.x;
newOffset.y = contentOffset.y;
//check push return in keyboar
if(!isScroll){
//180 is height of keyboar
newOffset.y += 180;
isScroll=YES;
}
[self.myScroll setContentOffset:newOffset animated:YES];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
//reset offset of content
isScroll = NO;
[self.myScroll setContentOffset:contentOffset animated:YES];
[textField endEditing:true];
return true;
}
#import "UIResponder+FirstResponder.h"
static __weak id currentFirstResponder;
@implementation UIResponder (FirstResponder)
+(id)currentFirstResponder {
currentFirstResponder = nil;
[[UIApplication sharedApplication] sendAction:@selector(findFirstResponder:) to:nil from:nil forEvent:nil];
return currentFirstResponder;
}
-(void)findFirstResponder:(id)sender {
currentFirstResponder = self;
}
@end
-(void)keyboardWillShowNotification:(NSNotification*)aNotification{
contentScrollView.delegate=nil;
contentScrollView.scrollEnabled=NO;
contentScrollViewOriginalOffset = contentScrollView.contentOffset;
UIResponder *lc_firstResponder = [UIResponder currentFirstResponder];
if([lc_firstResponder isKindOfClass:[UIView class]]){
UIView *lc_view = (UIView *)lc_firstResponder;
CGRect lc_frame = [lc_view convertRect:lc_view.bounds toView:contentScrollView];
CGPoint lc_point = CGPointMake(0, lc_frame.origin.y-lc_frame.size.height);
[contentScrollView setContentOffset:lc_point animated:YES];
}
}
-(void)keyboardWillHideNotification:(NSNotification*)aNotification{
contentScrollView.delegate=self;
contentScrollView.scrollEnabled=YES;
[contentScrollView setContentOffset:contentScrollViewOriginalOffset animated:YES];
}
class YourViewController: UIViewController, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
_yourTextField.delegate = self //make sure you have the delegate set to this view controller for each of your textFields so textFieldDidBeginEditing can be called for each one
...
}
func textFieldDidBeginEditing(textField: UITextField) {
var point = textField.convertPoint(textField.frame.origin, toView: _yourScrollView)
point.x = 0.0 //if your textField does not have an origin at 0 for x and you don't want your scrollView to shift left and right but rather just up and down
_yourScrollView.setContentOffset(point, animated: true)
}
func textFieldDidEndEditing(textField: UITextField) {
//Reset scrollview once done editing
scrollView.setContentOffset(CGPoint.zero, animated: true)
}
}