当键盘存在时如何使UITextField向上移动?
使用iPhone SDK:
我有一个带UITextFields
的UIView
,可以打开一个键盘。 我需要它能够:
-
键盘调出后,允许滚动
UIScrollView
的内容以查看其他文本字段 -
自动“跳转”(通过滚动)或缩短
我知道我需要一个UIScrollView
。 我试着改变我的UIView
类到一个UIScrollView
但我仍然无法向上或向下滚动文本框。
我是否需要UIView和UIScrollView
? 一个人进去了吗? [编辑:我现在知道你想要一个UIScrollView
里面的UIView
,诀窍是以编程方式将UIScrollView
的内容大小设置为UIView
的帧大小。]
那么为了自动滚动到活动文本字段需要执行什么?
理想情况下,尽可能多的组件设置将在Interface Builder中完成。 我只想写什么需要它的代码。
注意:我正在使用的UIView
(或UIScrollView
)是由一个标签栏( 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 }
但是,这并不会自动“向上移动”,也不会将可见区域中的较低文本字段居中,这是我真正想要的。
-
如果您现在拥有的内容不适合iPhone屏幕,则只需要一个ScrollView。 (如果将ScrollView添加为组件的超级视图,只是为了使TextField在键盘出现时向上滚动,则不需要)。
-
为了显示没有被键盘隐藏的文本字段,标准方法是在显示键盘时向上/向下移动具有文本字段的视图。
这是一些示例代码:
#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 above 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]; }
我还有一个由多个UITextFields
组成的UIScrollView
,其中一个或多个在被编辑时会被键盘遮住。
如果你的UIScrollView
没有正确的滚动,这里有一些事情要考虑。
1)确保你的contentSize大于UIScrollView
框架大小。 理解UIScrollViews
的方法是UIScrollView
就像contentSize中定义的内容的查看窗口。 所以当为了让UIScrollview
在任何地方滚动时,contentSize必须大于UIScrollView
。 否则,不需要滚动,因为contentSize中定义的所有内容都已经可见。 BTW,默认contentSize = CGSizeZero
。
2)现在你明白了UIScrollView
真的是你的“内容”的一个窗口,确保键盘不会遮住你的UIScrollView's
查看“窗口”的方法是调整UIScrollView
大小,这样当键盘出现时,有UIScrollView
窗口大小只是原来的UIScrollView
frame.size.height减去键盘的高度。 这将确保您的窗户只有那个小的可视区域。
3)这里是catch:当我第一次实现这个时,我想我得到编辑的文本字段的CGRect
并调用UIScrollView's
scrollRecToVisible方法。 我通过调用scrollRecToVisible
方法实现了UITextFieldDelegate
方法textFieldDidBeginEditing
。 这实际上与一个奇怪的副作用,即滚动将UITextField
捕捉到位。 最长的时间,我无法弄清楚是什么。 然后我注释了textFieldDidBeginEditing
委托方法,它都可以工作!!(???)。 事实certificate,我相信UIScrollView
实际上隐含地将当前编辑的UITextField
隐式UITextField
入可视窗口。 我的UITextFieldDelegate
方法的实现和后续调用scrollRecToVisible
是多余的,是造成奇怪副作用的原因。
所以这里是正确的滚动UITextField
在UIScrollView
位置,当键盘出现的步骤。
// 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:UIKeyboardFrameEndUserInfoKey] 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:UIKeyboardFrameEndUserInfoKey] 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; }
- 在
viewDidLoad
注册键盘通知 - 注销
viewDidUnload
的键盘nofitications - 确保
contentSize
被设置,并且比viewDidLoad
的UIScrollView
- 当键盘出现时收缩
UIScrollView
- 当键盘消失时恢复
UIScrollView
。 - 使用伊娃来检测键盘是否已经显示在屏幕上,因为每当
UITextField
被选中时键盘通知被发送,即使键盘已经存在,以避免在UIScrollView
已经收缩时缩小
有一点需要注意的是,即使键盘已经在屏幕上时, UIKeyboardWillShowNotification
会触发,当你在另一个UITextField
上选项卡。 我已经通过使用ivar来解决这个问题,以避免在键盘已经在屏幕上时调整UIScrollView
大小。 当键盘已经在那里时无意中调整UIScrollView
将会是灾难性的!
希望这个代码可以为你节省一些很多的头痛。
正如文档中所提供的那样,实际上最好使用苹果的实现。 但是,他们提供的代码是错误的。 将keyboardWasShown中发现的部分replace为下面的注释:
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)他们总是计算,如果点是在视图的框架,但它是一个滚动视图,所以它可能已经滚动,你需要考虑的偏移量:
origin.y -= scrollView.contentOffset.y
(2)他们将contentOffset移动了键盘的高度,但是我们想要的是相反的(我们想把contentOffset移动到屏幕上可见的高度,而不是那个)。
activeField.frame.origin.y-(aRect.size.height)
在textFieldDidBeginEditting
和[self animateTextField:textField up:YES]
像这样调用函数[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]; }
我希望这个代码能帮助你。
在Swift 2中
func animateTextField(textField: UITextField, up: Bool) { let movementDistance:CGFloat = -130 let movementDuration: Double = 0.3 var movement:CGFloat = 0 if up { movement = movementDistance } else { movement = -movementDistance } UIView.beginAnimations("animateTextField", context: nil) UIView.setAnimationBeginsFromCurrentState(true) UIView.setAnimationDuration(movementDuration) self.view.frame = CGRectOffset(self.view.frame, 0, movement) UIView.commitAnimations() } func textFieldDidBeginEditing(textField: UITextField) { self.animateTextField(textField, up:true) } func textFieldDidEndEditing(textField: UITextField) { self.animateTextField(textField, up:false) }
SWIFT 3
func animateTextField(textField: UITextField, up: Bool) { let movementDistance:CGFloat = -130 let movementDuration: Double = 0.3 var movement:CGFloat = 0 if up { movement = movementDistance } else { movement = -movementDistance } UIView.beginAnimations("animateTextField", context: nil) UIView.setAnimationBeginsFromCurrentState(true) UIView.setAnimationDuration(movementDuration) self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement) UIView.commitAnimations() } func textFieldDidBeginEditing(textField: UITextField) { self.animateTextField(textField: textField, up:true) } func textFieldDidEndEditing(textField: UITextField) { self.animateTextField(textField: textField, up:false) }
只需使用TextFields:
1a)使用Interface Builder
:select所有TextFields => Edit =>embedded在=> ScrollView
1b)在名为scrollView的UIScrollView中手动embeddedTextFields
2)设置UITextFieldDelegate
3)设置每个textField.delegate = self;
(或在Interface Builder
build立连接)
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]; }
我已经把一个通用的UIScrollView
, UITableView
,甚至UICollectionView
子类放在一起,负责移动键盘中的所有文本字段。
当键盘即将出现时,子类将find即将编辑的子视图,并调整其框架和内容偏移量,以确保视图可见,animation与键盘popup相匹配。 当键盘消失时,它会恢复原来的大小。
它应该基本上使用任何设置,无论是基于UITableView
的界面,还是由手动放置的视图组成。
这里是解决scheme : 移动文本字段的方式键盘的方式
对于通用解决scheme ,这是我实现IQKeyboardManager的方法 。
第一步: –我在单身类中添加了UITextField
, UITextView
和UIKeyboard
全局通知。 我称之为IQKeyboardManager 。
第2 UIKeyboardWillShowNotification
: –如果findUIKeyboardWillShowNotification
, UITextFieldTextDidBeginEditingNotification
或UITextViewTextDidBeginEditingNotification
通知,我尝试从UIWindow.rootViewController
层次结构中获取topMostViewController
实例。 为了正确地发现它的UITextField
/ UITextView
,需要调整topMostViewController.view
的框架。
第三步: –我计算了第一个响应的UITextField
/ UITextView
的topMostViewController.view
预期移动距离。
第四步: –根据预期的移动距离,向上/向下移动topMostViewController.view.frame
。
第5步 –如果findUIKeyboardWillHideNotification
, UITextFieldTextDidEndEditingNotification
或UITextViewTextDidEndEditingNotification
通知,我再次尝试从UIWindow.rootViewController
层次结构中获取topMostViewController
实例。
topMostViewController.view
步: –我计算了topMostViewController.view
干扰距离,需要恢复到原来的位置。
第7步: –我根据干扰的距离恢复topMostViewController.view.frame
。
第8步: –我在应用程序加载时实例化单例IQKeyboardManager类实例,所以应用程序中的每个UITextField
/ UITextView
将根据预期的移动距离自动调整。
这就是所有的IQKeyboardManager为你做的没有线的代码真的! 只需将相关的源文件拖放到项目即可。 IQKeyboardManager还支持设备方向 , 自动UIToolbarpipe理 , KeybkeyboardDistanceFromTextField以及远远超出您的想象。
对于Swift程序员:
这将为你做所有事情,只要把它们放在你的视图控制器类中,并实现UITextFieldDelegate
到你的视图控制器并设置textField的委托给self
textField.delegate = self // Setting delegate of your UITextField to self
实现委托callback方法:
func textFieldDidBeginEditing(textField: UITextField) { animateViewMoving(true, moveValue: 100) } func textFieldDidEndEditing(textField: UITextField) { animateViewMoving(false, moveValue: 100) } // Lifting the view up func animateViewMoving (up:Bool, moveValue :CGFloat){ let movementDuration:NSTimeInterval = 0.3 let movement:CGFloat = ( up ? -moveValue : moveValue) UIView.beginAnimations( "animateView", context: nil) UIView.setAnimationBeginsFromCurrentState(true) UIView.setAnimationDuration(movementDuration ) self.view.frame = CGRectOffset(self.view.frame, 0, movement) UIView.commitAnimations() }
已经有很多答案了,但是上面的解决scheme还是没有一个具有“完美”的无缺陷,向后兼容和无闪烁animation所需的所有花哨定位的东西。 (错误时框架/边界和contentOffsetanimation,不同的界面方向,iPad分离键盘,…)
让我分享我的解决scheme:
(假设你已经设置了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, eg 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]; }
I wanted to add a comment to Shiun's answer, but I apparently do not have enough rep points to add comments. Perhaps someone with more points can add a comment with the info below, then I'll delete this answer…
Shiun said "As it turned out, I believe the UIScrollView actually implicitly brings the currently edited UITextField into the viewable window implicitly" This seems to be true for iOS 3.1.3, but not 3.2, 4.0, or 4.1. I had to add an explicit scrollRectToVisible in order to make the UITextField visible on iOS >= 3.2.
This document details a solution to this problem. Look at the source code under 'Moving Content That Is Located Under the Keyboard'. It's pretty straightforward.
EDIT: Noticed there's a wee glitch in the example. You will probably want to listen for UIKeyboardWillHideNotification
instead of UIKeyboardDidHideNotification
. Otherwise the scroll view behind of the keyboard will be clipped for the duration of the keyboard closing animation.
One thing to consider is whether you ever want to use a UITextField
on its own. I haven't come across any well-designed iPhone apps that actually use UITextFields
outside of UITableViewCells
.
It will be some extra work, but I recommend you implement all data entry views a table views. Add a UITextView
to your UITableViewCells
.
Easiest solution found
- (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 = 80; // tweak as needed const float movementDuration = 0.3f; // tweak as needed int movement = (up ? -movementDistance : movementDistance); [UIView beginAnimations: @"anim" context: nil]; [UIView setAnimationBeginsFromCurrentState: YES]; [UIView setAnimationDuration: movementDuration]; self.view.frame = CGRectOffset(self.view.frame, 0, movement); [UIView commitAnimations]; }
Little fix that works for many UITextFields
#pragma mark UIKeyboard handling #define kMin 150 -(void)textFieldDidBeginEditing:(UITextField *)sender { if (currTextField) { [currTextField release]; } currTextField = [sender retain]; //move the main view, so that the keyboard does not hide it. if (self.view.frame.origin.y + currTextField.frame.origin. y >= kMin) { [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 above the keyboard // 2. increase the size of the view so that the area behind the keyboard is covered up. rect.origin.y = kMin - currTextField.frame.origin.y ; } else { // revert back to the normal state. rect.origin.y = 0; } self.view.frame = rect; [UIView commitAnimations]; } - (void)keyboardWillShow:(NSNotification *)notif { //keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately if ([currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y >= kMin) { [self setViewMovedUp:YES]; } else if (![currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y < kMin) { [self setViewMovedUp:NO]; } } - (void)keyboardWillHide:(NSNotification *)notif { //keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately if (self.view.frame.origin.y < 0 ) { [self setViewMovedUp:NO]; } } - (void)viewWillAppear:(BOOL)animated { // register for keyboard notifications [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:self.view.window]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:self.view.window]; } - (void)viewWillDisappear:(BOOL)animated { // unregister for keyboard notifications while not visible. [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; }
RPDP's code successfully moves the text field out of the way of the keyboard. But when you scroll to the top after using and dismissing the keyboard, the top has been scrolled up out of the view. This is true for the Simulator and the device. To read the content at the top of that view, one has to reload the view.
Isn't his following code supposed to bring the view back down?
else { // revert back to the normal state. rect.origin.y += kOFFSET_FOR_KEYBOARD; rect.size.height -= kOFFSET_FOR_KEYBOARD; }
I'm not sure if moving the view up is the correct approach, I did it in a differente way, resizing the UIScrollView. I explained it in details on a little article
To bring back to original view state, add:
-(void)textFieldDidEndEditing:(UITextField *)sender { //move the main view, so that the keyboard does not hide it. if (self.view.frame.origin.y < 0) { [self setViewMovedUp:NO]; } }
There so many solutions, but I've spend some hours before it start works. So, I put this code here (just paste to the project, any modifications needn't):
@interface RegistrationViewController : UIViewController <UITextFieldDelegate>{ UITextField* activeField; UIScrollView *scrollView; } @end - (void)viewDidLoad { [super viewDidLoad]; scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame]; //scrool view must be under main view - swap it UIView* natView = self.view; [self setView:scrollView]; [self.view addSubview:natView]; CGSize scrollViewContentSize = self.view.frame.size; [scrollView setContentSize:scrollViewContentSize]; [self registerForKeyboardNotifications]; } - (void)viewDidUnload { activeField = nil; scrollView = nil; [self unregisterForKeyboardNotifications]; [super viewDidUnload]; } - (void)registerForKeyboardNotifications { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShown:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil]; } -(void)unregisterForKeyboardNotifications { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; // unregister for keyboard notifications while not visible. [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } - (void)keyboardWillShown:(NSNotification*)aNotification { NSDictionary* info = [aNotification userInfo]; CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; CGRect frame = self.view.frame; frame.size.height -= kbSize.height; CGPoint fOrigin = activeField.frame.origin; fOrigin.y -= scrollView.contentOffset.y; fOrigin.y += activeField.frame.size.height; if (!CGRectContainsPoint(frame, fOrigin) ) { CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y + activeField.frame.size.height - frame.size.height); [scrollView setContentOffset:scrollPoint animated:YES]; } } - (void)keyboardWillBeHidden:(NSNotification*)aNotification { [scrollView setContentOffset:CGPointZero animated:YES]; } - (void)textFieldDidBeginEditing:(UITextField *)textField { activeField = textField; } - (void)textFieldDidEndEditing:(UITextField *)textField { activeField = nil; } -(BOOL) textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; return YES; }
PS: I hope the code help somebody make desired effect quickly. (Xcode 4.5)
try this short trick…
- (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 = textField.frame.origin.y / 2; // tweak as needed const float movementDuration = 0.3f; // tweak as needed int movement = (up ? -movementDistance : movementDistance); [UIView beginAnimations: @"anim" context: nil]; [UIView setAnimationBeginsFromCurrentState: YES]; [UIView setAnimationDuration: movementDuration]; self.view.frame = CGRectOffset(self.view.frame, 0, movement); [UIView commitAnimations]; }
Happy coding :)….
@user271753
To get your view back to original add:
-(BOOL)textFieldShouldReturn:(UITextField *)textField{ [textField resignFirstResponder]; [self setViewMovedUp:NO]; return YES; }
It doesn't require a scroll view to be able to move the view frame. You can change the frame of a viewcontroller's
view so that the entire view moves up just enough to put the firstresponder text field above the keyboard. When I ran into this problem I created a subclass of UIViewController
that does this. It observes for the keyboard will appear notification and finds the first responder subview and (if needed) it animates the main view upward just enough so that the first responder is above the keyboard. When the keyboard hides, it animates the view back where it was.
To use this subclass make your custom view controller a subclass of GMKeyboardVC and it inherits this feature (just be sure if you implement viewWillAppear
and viewWillDisappear
they must call super). The class is on github .
Here is the hack solution I came up with for a specific layout. This solution is similar to Matt Gallagher solution in that is scrolls a section into view. I am still new to iPhone development, and am not familiar with how the layouts work. Thus, this hack.
My implementation needed to support scrolling when clicking in a field, and also scrolling when the user selects next on the keyboard.
I had a UIView with a height of 775. The controls are spread out basically in groups of 3 over a large space. I ended up with the following IB layout.
UIView -> UIScrollView -> [UI Components]
Here comes the hack
I set the UIScrollView height to 500 units larger then the actual layout (1250). I then created an array with the absolute positions I need to scroll to, and a simple function to get them based on the IB Tag number.
static NSInteger stepRange[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 140, 140, 140, 140, 410 }; NSInteger getScrollPos(NSInteger i) { if (i < TXT_FIELD_INDEX_MIN || i > TXT_FIELD_INDEX_MAX) { return 0 ; return stepRange[i] ; }
Now all you need to do is use the following two lines of code in textFieldDidBeginEditing and textFieldShouldReturn (the latter one if you are creating a next field navigation)
CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ; [self.scrollView setContentOffset:point animated:YES] ;
An example.
- (void) textFieldDidBeginEditing:(UITextField *)textField { CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ; [self.scrollView setContentOffset:point animated:YES] ; } - (BOOL)textFieldShouldReturn:(UITextField *)textField { NSInteger nextTag = textField.tag + 1; UIResponder* nextResponder = [textField.superview viewWithTag:nextTag]; if (nextResponder) { [nextResponder becomeFirstResponder]; CGPoint point = CGPointMake(0, getScrollPos(nextTag)) ; [self.scrollView setContentOffset:point animated:YES] ; } else{ [textField resignFirstResponder]; } return YES ; }
This method does not 'scroll back' as other methods do. This was not a requirement. Again this was for a fairly 'tall' UIView, and I did not have days to learn the internal layout engines.
As per the docs , as of iOS 3.0, the UITableViewController
class automatically resizes and repositions its table view when there is in-line editing of text fields. I think it's not sufficient to put the text field inside a UITableViewCell
as some have indicated.
从文档 :
A table view controller supports inline editing of table view rows; if, for example, rows have embedded text fields in editing mode, it scrolls the row being edited above the virtual keyboard that is displayed.
Here I found the simplest solution to handle keypad.
You need to just copy-paste below sample code and change your textfield or any view which you want to move up.
步骤1
Just copy-paste below two method in your controller
- (void)registerForKeyboardNotifications { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil]; } - (void)deregisterFromKeyboardNotifications { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; }
第2步
register & deregister Keypad Notifications in viewWillAppear and viewWillDisappear methods respectively.
- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self registerForKeyboardNotifications]; } - (void)viewWillDisappear:(BOOL)animated { [self deregisterFromKeyboardNotifications]; [super viewWillDisappear:animated]; }
步骤3
Here comes the soul part, Just replace your textfield, and change height how much you want to move upside.
- (void)keyboardWasShown:(NSNotification *)notification { NSDictionary* info = [notification userInfo]; CGSize currentKeyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; //you need replace your textfield instance here CGPoint textFieldOrigin = self.tokenForPlaceField.frame.origin; CGFloat textFieldHeight = self.tokenForPlaceField.frame.size.height; CGRect visibleRect = self.view.frame; visibleRect.size.height -= currentKeyboardSize.height; if (!CGRectContainsPoint(visibleRect, textFieldOrigin)) { //you can add yor desired height how much you want move keypad up, by replacing "textFieldHeight" below CGPoint scrollPoint = CGPointMake(0.0, textFieldOrigin.y - visibleRect.size.height + textFieldHeight); //replace textFieldHeight to currentKeyboardSize.height, if you want to move up with more height [self.scrollView setContentOffset:scrollPoint animated:YES]; } } - (void)keyboardWillBeHidden:(NSNotification *)notification { [self.scrollView setContentOffset:CGPointZero animated:YES]; }
Reference : well, Please appreciate this guy , who shared this beautiful code snip, clean solution.
Hope this would be surly helpful someone out there.
Been searching for a good tutorial for beginners on the subject, found the best tutorial here .
In the MIScrollView.h
example at the bottom of the tutorial be sure to put a space at
@property (nonatomic, retain) id backgroundTapDelegate;
as you see.
When UITextField
is in a UITableViewCell
scrolling should be setup automatically.
If it is not it is probably because of incorrect code/setup of the tableview.
For example when i reloaded my long table with one UITextField
at the bottom as follows,
-(void) viewWillAppear:(BOOL)animated { [self.tableview reloadData]; }
then my textfield at the bottom was obscured by the keyboard which appeared when I clicked inside the textfield.
To fix this I had to do this –
-(void) viewWillAppear:(BOOL)animated { //add the following line to fix issue [super viewWillAppear:animated]; [self.tableview reloadData]; }
Use this third party you don't need to write even one line
https://github.com/hackiftekhar/IQKeyboardManager
download project and drag and drop IQKeyboardManager in your project. If you find any issue please read README document.
Guys really its remove headache to manage keyboard ..
Thanks and best of luck!
尝试这个:
-(void)textFieldDidBeginEditing:(UITextField *)sender { if ([sender isEqual:self.m_Sp_Contact]) { [self.m_Scroller setContentOffset:CGPointMake(0, 105)animated:YES]; } }
Note : this answer assumes your textField is in a scrollView.
I prefer to deal with this using scrollContentInset and scrollContentOffset instead of messing with the frames of my view.
First let's listen for the keyboard notifications
//call this from viewWillAppear -(void)addKeyboardNotifications { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; } //call this from viewWillDisappear -(void)removeKeyboardNotifications{ [[NSNotificationCenter default Center] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; }
Next step is to keep a property that represents the current first responder (UITextfield/ UITextVIew that currently has the keyboard).
We use the delegate methods to set this property. If you're using another component, you will need something similar.
Note that for textfield we set it in didBeginEditing and for textView in shouldBeginEditing. This is because textViewDidBeginEditing gets called after UIKeyboardWillShowNotification for some reason.
-(BOOL)textViewShouldBeginEditing:(UITextView * )textView{ self.currentFirstResponder = textView; return YES; } -(void)textFieldDidBeginEditing:(UITextField *)textField{ self.currentFirstResponder = textField; }
Finally, here's the magic
- (void)keyboardWillShow:(NSNotification*)aNotification{ NSDictionary* info = [aNotification userInfo]; CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; /*if currentFirstResponder is overlayed by the keyboard, move it so it bottom ends where the keyboard begins*/ if(self.currentFirstResponder){ //keyboard origin in currentFirstResponderFrame CGPoint keyboardOrigin = [self.currentFirstResponder convertPoint:kbFrame.origin fromView:nil]; float spaceBetweenFirstResponderAndKeyboard = abs(self.currentFirstResponder.frame.size.height-keyboardOrigin.y); //only scroll the scrollview if keyboard overlays the first responder if(spaceBetweenFirstResponderAndKeyboard>0){ //if i call setContentOffset:animate:YES it behaves differently, not sure why [UIView animateWithDuration:0.25 animations:^{ [self.scrollView setContentOffset:CGPointMake(0,self.scrollView.contentOffset.y+spaceBetweenFirstResponderAndKeyboard)]; }]; } } //set bottom inset to the keyboard height so you can still scroll the whole content UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbFrame.size.height, 0.0); _scrollView.contentInset = contentInsets; _scrollView.scrollIndicatorInsets = contentInsets; } - (void)keyboardWillHide:(NSNotification*)aNotification{ UIEdgeInsets contentInsets = UIEdgeInsetsZero; _scrollView.contentInset = contentInsets; _scrollView.scrollIndicatorInsets = contentInsets; }
This is the solution using Swift.
import UIKit class ExampleViewController: UIViewController, UITextFieldDelegate { @IBOutlet var scrollView: UIScrollView! @IBOutlet var textField1: UITextField! @IBOutlet var textField2: UITextField! @IBOutlet var textField3: UITextField! @IBOutlet var textField4: UITextField! @IBOutlet var textField5: UITextField! var activeTextField: UITextField! // MARK: - View override func viewDidLoad() { super.viewDidLoad() self.textField1.delegate = self self.textField2.delegate = self self.textField3.delegate = self self.textField4.delegate = self self.textField5.delegate = self } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) self.registerForKeyboardNotifications() } override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) self.unregisterFromKeyboardNotifications() } // MARK: - Keyboard // Call this method somewhere in your view controller setup code. func registerForKeyboardNotifications() { let center: NSNotificationCenter = NSNotificationCenter.defaultCenter() center.addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil) center.addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil) } func unregisterFromKeyboardNotifications () { let center: NSNotificationCenter = NSNotificationCenter.defaultCenter() center.removeObserver(self, name: UIKeyboardDidShowNotification, object: nil) center.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil) } // Called when the UIKeyboardDidShowNotification is sent. func keyboardWasShown (notification: NSNotification) { let info : NSDictionary = notification.userInfo! let kbSize = (info.objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue() as CGRect!).size let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0); scrollView.contentInset = contentInsets; scrollView.scrollIndicatorInsets = contentInsets; // If active text field is hidden by keyboard, scroll it so it's visible // Your app might not need or want this behavior. var aRect = self.view.frame aRect.size.height -= kbSize.height; if (!CGRectContainsPoint(aRect, self.activeTextField.frame.origin) ) { self.scrollView.scrollRectToVisible(self.activeTextField.frame, animated: true) } } // Called when the UIKeyboardWillHideNotification is sent func keyboardWillBeHidden (notification: NSNotification) { let contentInsets = UIEdgeInsetsZero; scrollView.contentInset = contentInsets; scrollView.scrollIndicatorInsets = contentInsets; } // MARK: - Text Field func textFieldDidBeginEditing(textField: UITextField) { self.activeTextField = textField } func textFieldDidEndEditing(textField: UITextField) { self.activeTextField = nil } }