ios - 键盘出现时如何使UITextField上移动

  显示原文与译文双语对照的内容

使用 iPhone SDK:

我有一个 UIViewUITextFields 引出了一个键盘。 我需要它才能:

  1. 允许滚动 UIScrollView的内容以在键盘启动后查看其他文本字段

  2. 自动"跳转"( 通过向上滚动) 或者缩短

我知道我需要一个 UIScrollView 。 我试图将 UIView的类更改为 UIScrollView,但仍无法向上或者向下滚动文本框。

我需要UIView和 UIScrollView? 一个进入另一个? [ 编辑:我现在知道你想要一个 UIScrollView 内的UIView,窍门是以编程方式将 UIScrollView的内容大小设置为 UIView的框架大小。]

然后需要实现什么才能自动滚动到活动文本字段?

理想情况下,尽可能多的组件设置将在接口生成器中完成。 我想只编写需要它的代码。

注意:我正在使用的UIView ( 或者 UIScrollView ) 是由一个 tabbar ( UITabBar ) 提出的,它需要正常运行。


编辑:我正在添加滚动条,只是当键盘出现时。 尽管它不是必需的,但我觉得它提供了一个更好的界面,因为用户可以滚动和改变文本框,例如。

当键盘向上和向下移动时,我就可以改变 UIScrollView的帧大小。 我只是在使用:


-(void)textFieldDidBeginEditing:(UITextField *)textField { 
//Keyboard becomes visible
 scrollView.frame = CGRectMake(scrollView.frame.origin.x, 
 scrollView.frame.origin.y, 
scrollView.frame.size.width,
scrollView.frame.size.height - 215 + 50);//resize
}

-(void)textFieldDidEndEditing:(UITextField *)textField {
//keyboard will hide
 scrollView.frame = CGRectMake(scrollView.frame.origin.x, 
 scrollView.frame.origin.y, 
 scrollView.frame.size.width,
 scrollView.frame.size.height + 215 - 50);//resize
}

但是,这不会自动"上移"或者居中显示在可见区域中的文本字段,这就是我真正想要的。

时间:

  1. 如果你现在的内容不适用于iPhone屏幕,你需要一个滚动视图。 ( 如果你正在添加滚动视图,以便在键盘出现时向上滚动滚动区,则不需要它。)

  2. 为了显示键盘而不被键盘隐藏,标准的方法是在键盘显示时向上/向下移动带有textfields的视图。

下面是一些示例代码:


#define kOFFSET_FOR_KEYBOARD 80.0

-(void)keyboardWillShow {
//Animate the current view out of the way
 if (self.view.frame.origin.y> = 0)
 {
 [self setViewMovedUp:YES];
 }
 else if (self.view.frame.origin.y <0)
 {
 [self setViewMovedUp:NO];
 }
}

-(void)keyboardWillHide {
 if (self.view.frame.origin.y> = 0)
 {
 [self setViewMovedUp:YES];
 }
 else if (self.view.frame.origin.y <0)
 {
 [self setViewMovedUp:NO];
 }
}

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
 if ([sender isEqual:mailTf])
 {
//move the main view, so that the keyboard does not hide it.
 if (self.view.frame.origin.y> = 0)
 {
 [self setViewMovedUp:YES];
 }
 }
}

//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
 [UIView beginAnimations:nil context:NULL];
 [UIView setAnimationDuration:0.3];//if you want to slide up the view

 CGRect rect = self.view.frame;
 if (movedUp)
 {
//1. move the view's origin up so that the text field that will be hidden come 上面 the keyboard 
//2. increase the size of the view so that the area behind the keyboard is covered up.
 rect.origin.y -= kOFFSET_FOR_KEYBOARD;
 rect.size.height += kOFFSET_FOR_KEYBOARD;
 }
 else
 {
//revert back to the normal state.
 rect.origin.y += kOFFSET_FOR_KEYBOARD;
 rect.size.height -= kOFFSET_FOR_KEYBOARD;
 }
 self.view.frame = rect;

 [UIView commitAnimations];
}


- (void)viewWillAppear:(BOOL)animated
{
 [super viewWillAppear:animated];
//register for keyboard notifications
 [[NSNotificationCenter defaultCenter] addObserver:self
 selector:@selector(keyboardWillShow)
 name:UIKeyboardWillShowNotification
 object:nil];

 [[NSNotificationCenter defaultCenter] addObserver:self
 selector:@selector(keyboardWillHide)
 name:UIKeyboardWillHideNotification
 object:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
 [super viewWillDisappear:animated];
//unregister for keyboard notifications while not visible.
 [[NSNotificationCenter defaultCenter] removeObserver:self
 name:UIKeyboardWillShowNotification
 object:nil];

 [[NSNotificationCenter defaultCenter] removeObserver:self
 name:UIKeyboardWillHideNotification
 object:nil];
}

我也有很多 UIScrollView 组成的问题,其中一个或者多个 UITextFields 在编辑时会被键盘遮住。

如果你的UIScrollView 没有正确滚动,需要考虑一些事项。

1 ) 确保你的contentSize比 UIScrollView 框架大。 理解 UIScrollViews的方法是 UIScrollView 就像在contentSize中定义的内容一样。 因此,为了让 UIScrollView 滚动任何地方,contentSize必须大于 UIScrollView 。 否则,不需要滚动,因为contentSize中定义的所有内容都已经可见。 默认的contentSize = CGSizeZero

2 )现在你明白到"内容" UIScrollView 真的是一个窗口,确保键盘不模糊 UIScrollView's 观看"窗口"是调整 UIScrollView 所以当键盘,你有 UIScrollView 窗口大小的原始 UIScrollView frame.size.height -键盘的高度。 这将确保你的窗口只有很小的可视区域。

3 ) 这里是捕捉:当我第一次实现它时,我想我必须得到编辑过的textfield的CGRect 并调用 UIScrollView's scrollRecToVisible方法。 我实现了 UITextFieldDelegate 方法 textFieldDidBeginEditing,调用了 scrollRecToVisible 方法。 这实际上与一种奇怪的副作用,滚动将吸附 UITextField 到位。 最长时间我无法弄清楚是什么。 然后我注释掉了 textFieldDidBeginEditing 委托方法,它全部工作() 。 ! 结果,我相信 UIScrollView 实际上隐式地将当前编辑的UITextField 隐式地置于可见窗口中。 我的UITextFieldDelegate 方法的实现和对 scrollRecToVisible的后续调用是多余的,这是产生奇怪副作用的原因。

下面是在键盘出现时适当地将 UITextField 滚动到位置的步骤。


//Implement viewDidLoad to do additional setup after loading the view, typically from a nib.


- (void)viewDidLoad 
{
 [super viewDidLoad];

//register for keyboard notifications
 [[NSNotificationCenter defaultCenter] addObserver:self 
 selector:@selector(keyboardWillShow:) 
 name:UIKeyboardWillShowNotification 
 object:self.view.window];
//register for keyboard notifications
 [[NSNotificationCenter defaultCenter] addObserver:self 
 selector:@selector(keyboardWillHide:) 
 name:UIKeyboardWillHideNotification 
 object:self.view.window];
 keyboardIsShown = NO;
//make contentSize bigger than your scrollSize (you will need to figure out for your own use case)
 CGSize scrollContentSize = CGSizeMake(320, 345);
 self.scrollView.contentSize = scrollContentSize;
 }

 - (void)keyboardWillHide:(NSNotification *)n
 {
 NSDictionary* userInfo = [n userInfo];

//get the size of the keyboard
 CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;


//resize the scrollview
 CGRect viewFrame = self.scrollView.frame;
//I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
 viewFrame.size.height += (keyboardSize.height - kTabBarHeight);

 [UIView beginAnimations:nil context:NULL];
 [UIView setAnimationBeginsFromCurrentState:YES];
 [self.scrollView setFrame:viewFrame];
 [UIView commitAnimations];

 keyboardIsShown = NO;
 }

- (void)keyboardWillShow:(NSNotification *)n
{
//This is an ivar I'm using to ensure that we do not do the frame size adjustment on the `UIScrollView` if the keyboard is already shown. This can happen if the user, after fixing editing a `UITextField`, scrolls the resized `UIScrollView` to another `UITextField` and attempts to edit the next `UITextField`. If we were to resize the `UIScrollView` again, it would be disastrous. NOTE: The keyboard notification will fire even when the keyboard is already shown.
 if (keyboardIsShown) {
 return;
 }

 NSDictionary* userInfo = [n userInfo];

//get the size of the keyboard
 CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

//resize the noteView
 CGRect viewFrame = self.scrollView.frame;
//I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
 viewFrame.size.height -= (keyboardSize.height - kTabBarHeight);

 [UIView beginAnimations:nil context:NULL];
 [UIView setAnimationBeginsFromCurrentState:YES];
 [self.scrollView setFrame:viewFrame];
 [UIView commitAnimations];
 keyboardIsShown = YES;
}

  1. viewDidLoad 处注册键盘通知
  2. 为键盘nofitications在 viewDidUnload 处注销
  3. 确保 contentSize 已经设置且大于 UIScrollView
  4. 收缩 UIScrollView 当键盘
  5. 恢复 UIScrollView 当键盘消失。
  6. 使用一个ivar检测如果键盘已经显示在屏幕上自键盘发送通知每次 UITextField 标签即使键盘已经存在时避免萎缩 UIScrollView 已经萎缩

有一点要注意的是, UIKeyboardWillShowNotification 将火即使屏幕上的键盘已经当你在另一个 UITextField 选项卡。 我通过使用一个rect来解决这个问题,当键盘已经在屏幕上时,它避免了改变 UIScrollView 。 当键盘已经存在时,意外调整 UIScrollView的大小会灾难性 !

希望这段代码能让你感到头痛。

实际上最好是使用苹果的实现,如文档中提供的那样。 但是,他们提供的代码是错误的。 替换在keyboardWasShown中找到的部分: 在下面的注释下面:


NSDictionary* info = [aNotification userInfo];
CGRect keyPadFrame=[[UIApplication sharedApplication].keyWindow convertRect:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue] fromView:self.view];
CGSize kbSize =keyPadFrame.size;
CGRect activeRect=[self.view convertRect:activeField.frame fromView:activeField.superview];
CGRect aRect = self.view.bounds;
aRect.size.height -= (kbSize.height);

CGPoint origin = activeRect.origin;
origin.y -= backScrollView.contentOffset.y;
if (!CGRectContainsPoint(aRect, origin)) {
 CGPoint scrollPoint = CGPointMake(0.0,CGRectGetMaxY(activeRect)-(aRect.size.height));
 [backScrollView setContentOffset:scrollPoint animated:YES];
}

苹果代码的问题如下: ( 1 ) 它们总是计算这个点是否在视图的框架内,但它是一个 ScrollView,所以它可能已经滚动,你需要考虑那个偏移量:


origin.y -= scrollView.contentOffset.y

( 2 ) 按键盘的高度移动 contentOffset,但我们需要相反的( 我们想按屏幕上可见的高度来移动 contentOffset,而不是't:


activeField.frame.origin.y-(aRect.size.height)

textFieldDidBeginEdittingtextFieldDidEndEditing 中调用函数 [self animateTextField:textField up:YES] 就像这样:


-(void)textFieldDidBeginEditing:(UITextField *)textField 
{ 
 [self animateTextField:textField up:YES]; 
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
 [self animateTextField:textField up:NO];
}

-(void)animateTextField:(UITextField*)textField up:(BOOL)up
{
 const int movementDistance = -130;//tweak as needed
 const float movementDuration = 0.3f;//tweak as needed

 int movement = (up? movementDistance : -movementDistance); 

 [UIView beginAnimations: @"animateTextField" context: nil];
 [UIView setAnimationBeginsFromCurrentState: YES];
 [UIView setAnimationDuration: movementDuration];
 self.view.frame = CGRectOffset(self.view.frame, 0, movement);
 [UIView commitAnimations];
}

我希望这个代码能帮助你。

只用textfields字段:

1 ) 使用 Interface Builder: 选择所有 TextFields => 编辑=> 嵌入=> ScrollView

1b ) 手动嵌入UIScrollView中名为scrollView的字符

2 ) 设置 UITextFieldDelegate

3 ) 设置每个 textField.delegate = self; ( 或者在 Interface Builder 中建立连接)

4 ) 复制/粘贴:


- (void)textFieldDidBeginEditing:(UITextField *)textField {
 CGPoint scrollPoint = CGPointMake(0, textField.frame.origin.y);
 [scrollView setContentOffset:scrollPoint animated:YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
 [scrollView setContentOffset:CGPointZero animated:YES];
}

我把一个通用的drop-in UIScrollViewUITableViewUICollectionView 子类放在一起,它负责将所有文本字段移出键盘。

当键盘即将出现时,子类会找到将要编辑的subview,并调整它的框架和内容偏移量,以确保视图可见,并使用动画来匹配键盘 pop-up 。 当键盘消失时,它恢复原来的大小。

它应该基本上可以与任何设置,一个 UITableView -based接口或者一个由手动放置的视图一起工作。

这里是:解决方案,用于将文本字段移出键盘编辑器

看看这个,不用麻烦你了。

TPKeyboardAvoiding

我想添加一个评论shiun回答,但我显然没有足够的代表点添加注释。 也许有更多点数的人可以用下面的信息添加评论,然后我将删除这个答案。。

Shiun说"第一,as it outthe面临UIScrollViewactually相信目前uitextfield编辑implicitly国家形象将会如何成为《viewable温多implicitly"对 3.1.3,但不是 3.2,4.0或者 4.1而言似乎是真的。 我必须添加一个显式scrollRectToVisible为了使ios> = 3.2uitextfield可见。

已经有了很多答案,但是上面的解决方案还没有一个"完美"bug-free所需的所有花哨的定位内容,向后兼容和flicker-free动画。 ( Bug 当帧/边界和contentOffset一起播放时,不同的界面方向,iPad拆分键盘,- )
让我分享我的解决方案:
( 假设你已经设置了 UIKeyboardWill(Show|Hide)Notification )


//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 上面 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];
}

这里文档详细描述这里问题的解决方案。 查看'移动位于键盘下的内容'下的源代码。 这很直截了当.

编辑:注意到这个例子有一个小问题。 你可能需要侦听 UIKeyboardWillHideNotification 而不是 UIKeyboardDidHideNotification 。 否则键盘的滚动视图将在键盘关闭动画期间被剪辑。

...