Move UIView up when the keyboard appears in iOS
我有一个uiview,它不在uiscrollview中。当键盘出现时,我想向上移动我的视图。在我尝试使用此解决方案之前:当键盘存在时,如何使uitextfield向上移动?.
它工作得很好。但是,在将数据插入文本字段后,它会将我带到另一个视图,当我回到这个页面时,它会跳起来,我看不到我的文本字段。这个问题有更好的解决办法吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [[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]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } #pragma mark - keyboard movements - (void)keyboardWillShow:(NSNotification *)notification { CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; [UIView animateWithDuration:0.3 animations:^{ CGRect f = self.view.frame; f.origin.y = -keyboardSize.height; self.view.frame = f; }]; } -(void)keyboardWillHide:(NSNotification *)notification { [UIView animateWithDuration:0.3 animations:^{ CGRect f = self.view.frame; f.origin.y = 0.0f; self.view.frame = f; }]; } |
使用以下代码显示和隐藏键盘
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | //Declare a delegate, assign your textField to the delegate and then include these methods -(BOOL)textFieldShouldBeginEditing:(UITextField *)textField { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil]; return YES; } - (BOOL)textFieldShouldEndEditing:(UITextField *)textField { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil]; [self.view endEditing:YES]; return YES; } - (void)keyboardDidShow:(NSNotification *)notification { // Assign new frame to your view [self.view setFrame:CGRectMake(0,-110,320,460)]; //here taken -110 for example i.e. your view will be scrolled to -110. change its value according to your requirement. } -(void)keyboardDidHide:(NSNotification *)notification { [self.view setFrame:CGRectMake(0,0,320,460)]; } |
我在
这里面嵌入了一堆与我的表单和布局相适应的神奇数字,可能不适合你的表单和布局,所以我鼓励你调整这些数字,以满足你的特定需求。
uiview+表单滚动.h:
1 2 3 4 5 6 7 8 9 | #import <Foundation/Foundation.h> @interface UIView (FormScroll) -(void)scrollToY:(float)y; -(void)scrollToView:(UIView *)view; -(void)scrollElement:(UIView *)view toPoint:(float)y; @end |
uiview+表单滚动.m:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #import"UIView+FormScroll.h" @implementation UIView (FormScroll) -(void)scrollToY:(float)y { [UIView beginAnimations:@"registerScroll" context:NULL]; [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [UIView setAnimationDuration:0.4]; self.transform = CGAffineTransformMakeTranslation(0, y); [UIView commitAnimations]; } -(void)scrollToView:(UIView *)view { CGRect theFrame = view.frame; float y = theFrame.origin.y - 15; y -= (y/1.7); [self scrollToY:-y]; } -(void)scrollElement:(UIView *)view toPoint:(float)y { CGRect theFrame = view.frame; float orig_y = theFrame.origin.y; float diff = y - orig_y; if (diff < 0) { [self scrollToY:diff]; } else { [self scrollToY:0]; } } @end |
将其导入到uiviewcontroller中,然后可以
1 2 3 4 5 6 7 8 9 10 | - (void)textFieldDidBeginEditing:(UITextField *)textField { [self.view scrollToView:textField]; } -(void) textFieldDidEndEditing:(UITextField *)textField { [self.view scrollToY:0]; [textField resignFirstResponder]; } |
…或者别的什么。这个类别提供了三种很好的方法来调整视图的位置。
将视图绑定到键盘也是一个选项(参见答案底部的gif)
斯威夫特4使用扩展:(未完全测试)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | extension UIView{ func bindToKeyboard(){ NotificationCenter.default.addObserver(self, selector: #selector(UIView.keyboardWillChange(notification:)), name: Notification.Name.UIKeyboardWillChangeFrame, object: nil) } func unbindToKeyboard(){ NotificationCenter.default.removeObserver(self, name: Notification.Name.UIKeyboardWillChangeFrame, object: nil) } @objc func keyboardWillChange(notification: Notification) { let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue let deltaY = targetFrame.origin.y - curFrame.origin.y UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: { self.frame.origin.y+=deltaY },completion: nil) } } |
斯威夫特2 + 3
使用扩展名:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | extension UIView{ func bindToKeyboard(){ NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(UIView.keyboardWillChange(_:)), name: UIKeyboardWillChangeFrameNotification, object: nil) } func keyboardWillChange(notification: NSNotification) { let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue() let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue() let deltaY = targetFrame.origin.y - curFrame.origin.y UIView.animateKeyframesWithDuration(duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: { self.frame.origin.y+=deltaY },completion: nil) } } |
用途:
1 2 | // view did load... textField.bindToKeyboard() |
…
1 2 | // view unload textField.unbindToKeyboard() |
结果:
重要的当视图卸载时,不要忘记移除观察者。
I found theDuncs answer very useful and below you can find my own (refactored) version:
主要变化
1 2 3 4 5 6 7 8 9 | - (void)viewWillAppear:(BOOL)animated { [[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 { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | - (void)keyboardWillShow:(NSNotification *)notification { CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; float newVerticalPosition = -keyboardSize.height; [self moveFrameToVerticalPosition:newVerticalPosition forDuration:0.3f]; } - (void)keyboardWillHide:(NSNotification *)notification { [self moveFrameToVerticalPosition:0.0f forDuration:0.3f]; } - (void)moveFrameToVerticalPosition:(float)position forDuration:(float)duration { CGRect frame = self.view.frame; frame.origin.y = position; [UIView animateWithDuration:duration animations:^{ self.view.frame = frame; }]; } |
基于Daniel Krom的解决方案。这是Swift 3.0中的版本。使用自动布局非常好,当键盘出现时,可以移动整个视图。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | extension UIView { func bindToKeyboard(){ NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil) } func unbindFromKeyboard(){ NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil) } @objc func keyboardWillChange(notification: NSNotification) { guard let userInfo = notification.userInfo else { return } let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as! Double let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as! UInt let curFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue let targetFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue let deltaY = targetFrame.origin.y - curFrame.origin.y UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: { self.frame.origin.y += deltaY }) } } |
如何使用它:在
1 2 3 4 5 | override func viewDidLoad() { super.viewDidLoad() view.bindToKeyboard() } |
在设计中增加
1 2 3 | deinit { view.unbindFromKeyboard() } |
试试这个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector (keyboardDidShow:) name: UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector (keyboardDidHide:) name: UIKeyboardDidHideNotification object:nil]; -(void) keyboardDidShow: (NSNotification *)notif { CGSize keyboardSize = [[[notif userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height+[self getTableView].tableFooterView.frame.size.height, 0.0); [self getTableView].contentInset = contentInsets; [self getTableView].scrollIndicatorInsets = contentInsets; CGRect rect = self.frame; rect.size.height -= keyboardSize.height; if (!CGRectContainsPoint(rect, self.frame.origin)) { CGPoint scrollPoint = CGPointMake(0.0, self.frame.origin.y - (keyboardSize.height - self.frame.size.height)); [[self getTableView] setContentOffset:scrollPoint animated:YES]; } } -(void) keyboardDidHide: (NSNotification *)notif { UIEdgeInsets contentInsets = UIEdgeInsetsZero; [self getTableView].contentInset = contentInsets; [self getTableView].scrollIndicatorInsets = contentInsets; } |
对于所有与键盘相关的问题,只需使用iqkeyboardmanager这很有帮助。https://github.com/hackiftekhar/iqkeyboardmanager.
基于UNC的答案,但使用自动布局的快速书写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | @IBOutlet weak var bottomConstraint: NSLayoutConstraint! // connect the bottom of the view you want to move to the bottom layout guide override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ConversationViewController.keyboardWillShow(_:)), name: UIKeyboardWillShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ConversationViewController.keyboardWillHide(_:)), name: UIKeyboardWillHideNotification, object: nil) } override func viewWillDisappear(animated: Bool) { NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil) NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil) super.viewWillDisappear(animated) } // MARK: - Keyboard events func keyboardWillShow(notification: NSNotification) { if let userInfo = notification.userInfo, keyboardFrame = userInfo[UIKeyboardFrameBeginUserInfoKey] { let keyboardSize = keyboardFrame.CGRectValue().size self.bottomConstraint.constant = keyboardSize.height UIView.animateWithDuration(0.3) { self.view.layoutIfNeeded() } } } func keyboardWillHide(notification: NSNotification) { self.bottomConstraint.constant = 0 UIView.animateWithDuration(0.3) { self.view.layoutIfNeeded() } } |
我实现了一个自定义控制器,它动态地计算键盘的大小,在键盘出现和消失时滚动文本字段,甚至在设备旋转期间也是如此。适用于所有iOS设备。只需继承控制器就可以得到所需的。您可以在以下链接找到所有说明:https://github.com/mikthebig/ios-textfield-scroll
不添加操作员通知的简单解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | -(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)textFieldDidEndEditing:(UITextField *)sender { if (self.view.frame.origin.y >= 0) { [self setViewMovedUp:NO]; } } -(void)textFieldDidBeginEditing:(UITextField *)sender { //move the main view, so that the keyboard does not hide it. if (self.view.frame.origin.y >= 0) { [self setViewMovedUp:YES]; } } |
在哪里?
1 | #define kOFFSET_FOR_KEYBOARD 80.0 |
斯威夫特4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: .UIKeyboardWillChangeFrame, object: nil) @objc func keyboardWillChange(notification: NSNotification) { let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue let deltaY = targetFrame.origin.y - curFrame.origin.y UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: { self.YourView.frame.origin.y+=deltaY },completion: nil) } |
干得好。不过,我在uiview中使用了这个代码。您应该能够对ScrollView进行这些调整。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | func addKeyboardNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil) } func keyboardWillShow(notification: NSNotification) { if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double // if using constraints // bottomViewBottomSpaceConstraint.constant = keyboardSize.height self.view.frame.origin.y -= keyboardSize.height UIView.animate(withDuration: duration) { self.view.layoutIfNeeded() } } } func keyboardWillHide(notification: NSNotification) { let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double //if using constraint // bottomViewBottomSpaceConstraint.constant = 0 self.view.frame.origin.y = 0 UIView.animate(withDuration: duration) { self.view.layoutIfNeeded() } } |
不要忘记在正确的位置删除通知。
1 2 3 4 | func removeKeyboardNotifications() { NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil) } |
为了防止有人用swift寻找解决方案,请将其放入您的代码中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: .UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: .UIKeyboardWillHide, object: nil) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil) NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil) } @objc func keyboardWillShow(notification: Notification) { if let userInfo = notification.userInfo { if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue { UIView.animate(withDuration: 0.3) { var alteredFrame = self.view.frame alteredFrame.origin.y = -keyboardSize.height self.view.frame = alteredFrame } } } } @objc func keyboardWillHide(notification: Notification) { UIView.animate(withDuration: 0.3) { var alteredFrame = self.view.frame alteredFrame.origin.y = 0.0 self.view.frame = alteredFrame } } |
声明委托,将文本字段分配给委托,然后包括这些方法。
假设您有一个带有电子邮件和密码文本字段的登录表单,则此代码将完全适合:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self.emailTextField resignFirstResponder]; [self.passwordTextField resignFirstResponder]; } - (BOOL)textFieldShouldReturn:(UITextField *)textField { if (self.emailTextField == textField) { [self.passwordTextField becomeFirstResponder]; } else { [self.emailTextField resignFirstResponder]; [self.passwordTextField resignFirstResponder]; } return NO; } - (void)viewWillAppear:(BOOL)animated { [[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 { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } #pragma mark - keyboard movements - (void)keyboardWillShow:(NSNotification *)notification { CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; [UIView animateWithDuration:0.3 animations:^{ CGRect f = self.view.frame; f.origin.y = -0.5f * keyboardSize.height; self.view.frame = f; }]; } -(void)keyboardWillHide:(NSNotification *)notification { [UIView animateWithDuration:0.3 animations:^{ CGRect f = self.view.frame; f.origin.y = 0.0f; self.view.frame = f; }]; } |
我刚刚创建了一个轻量级的键盘处理程序来跟踪键盘框架。
用途:
1 2 3 4 5 6 | self.keyboardHandler = [EDKeyboardHandler new]; [self.keyboardHandler listenWithBlock:^(KeyboardInfo *model) { //adjust view positions according to keyboard position here }]; |
键盘信息模型具有以下特性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | typedef enum : NSUInteger { KeyboardStatusDidShow, KeyboardStatusWillShow, KeyboardStatusDidHide, KeyboardStatusWillHide, } KeyboardStatus; @interface KeyboardInfo:NSObject @property (nonatomic,readonly) NSTimeInterval animationDuration; @property (nonatomic,readonly) CGRect keyboardFrame; @property (nonatomic,readonly) NSInteger animationCurve; @property (nonatomic,readonly) KeyboardStatus status; @end |
查看Github项目以了解详细信息和Cocoapods集成。
如果
注意:桌子的位置和尺寸必须合理。例如:
- 如果从视图底部开始计算表的Y位置为100,则300高度键盘将覆盖整个表。
- 如果当键盘出现时,表的
height = 10 和其中的textfield 必须向上滚动100才能可见,则该文本字段将超出表的界限。