当键盘出现在 iOS 中时,将 UIView 向上移动

我有一个 UIView,它不在 UIScrollView 里面。当键盘出现时,我想向上移动我的视图。在我尝试使用这个解决方案之前: 当键盘存在时,如何使 UITextField 向上移动?

一切正常。但是在文本字段中插入数据之后,它将我带到另一个视图,当我回到这个页面时,它只是跳转,我看不到我的文本字段。对这个问题还有更好的解决办法吗?

113945 次浏览

我在 UIView上写了一个小分类,管理临时滚动的东西,而不需要把整个东西打包成一个 UIScrollView。我在这里使用动词“滚动”可能并不理想,因为它可能会让你认为这里涉及到滚动视图,而实际上并没有——我们只是在动画一个 UIView(或者 UIView子类)的位置。

有一堆神奇的数字嵌入在这是适合我的形式和布局,可能不适合你的,所以我鼓励调整这适合你的具体需要。

UIView + FormScroll.h:

#import <Foundation/Foundation.h>


@interface UIView (FormScroll)


-(void)scrollToY:(float)y;
-(void)scrollToView:(UIView *)view;
-(void)scrollElement:(UIView *)view toPoint:(float)y;


@end

UIView + FormScroll.m:

#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 中,然后就可以这样做了

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self.view scrollToView:textField];
}


-(void) textFieldDidEndEditing:(UITextField *)textField
{
[self.view scrollToY:0];
[textField resignFirstResponder];
}

... 或者别的什么,这个类别给了你三种很好的方法来调整视角的位置。

使用以下代码显示和隐藏键盘

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

我已经实现了一个自定义控制器,它可以动态计算键盘的大小,在显示和消失时滚动 textFields,甚至在设备旋转时也是如此。适用于所有 iOS 设备。只需简单地继承控制器就可以得到您需要的东西。你可以在下面的链接找到它和所有的说明: https://github.com/mikthebig/ios-textfield-scroll

- (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;
}];
}

不需要添加观察者通知的简单解决方案

-(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];
}
}

在哪里

#define kOFFSET_FOR_KEYBOARD 80.0

我发现 笨蛋回答非常有用,你可以在下面找到我自己的(重构的)版本:


主要变化

  1. 现在得到的是 动态调整键盘大小,而不是硬编码值
  2. 将 UIView 动画提取到自己的 防止重复代码方法中
  3. 允许将持续时间传递到方法中,而不是硬编码

- (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];
}

- (void)keyboardWillShow:(NSNotification *)notification {
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] 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;
}];
}

对于所有与键盘相关的问题,只需使用 IQKeyBoardManager 很有帮助。 Https://github.com/hackiftekhar/iqkeyboardmanager.

试试这个:-

 [[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;
}

如果 textfield位于表的单元格中(即使是在 table.scrollable = NO中) ,也可以执行 容易和自动

注意: 桌子的位置和大小必须合理。例如:

  • 如果从视图的底部开始计算表的 y 位置为100,那么300高度的键盘将重叠整个表。
  • 如果表的 height = 10,并且其中的 textfield在键盘出现时必须向上滚动100以便可见,那么该文本字段将超出表的范围。

声明委托,将文本字段分配给委托,然后包括这些方法。

假设您有一个带有电子邮件和密码文本字段的登录表单,这段代码将非常适合:

-(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;
}];
}

我刚刚创建了一个轻量级的键盘处理程序来跟随键盘框架。

用法:

 self.keyboardHandler = [EDKeyboardHandler new];


[self.keyboardHandler listenWithBlock:^(KeyboardInfo *model)
{
//adjust view positions according to keyboard position here
}];

KeyboardInfo 模型具有以下属性:

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项目的详细信息和 cocopods 集成。

将视图绑定到键盘也是一个选项(参见答案底部的 GIF)

Swift 4

使用扩展名: (未完全测试)

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)


}
}

Swift 2 + 3

使用扩展:

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)


}
}

用法:

// view did load...
textField.bindToKeyboard()

...

// view unload
textField.unbindToKeyboard()

结果:
enter image description here

很重要
在视图卸载时,不要忘记删除观察者

邓肯的回答为基础,但用自动布局的 Swift 编写。

@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()
}
}

基于丹尼尔 · 克罗姆的解决方案。这是 Swift 3.0的版本。与 自动版式工程伟大,并移动整个视图时键盘出现。

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
})
}
}

使用方法: 在 viewDidLoad中添加 bindToKeyboard函数,如:

override func viewDidLoad() {
super.viewDidLoad()


view.bindToKeyboard()
}

在 deinit 中添加 unbindFromKeyboard函数:

deinit {
view.unbindFromKeyboard()
}

给你。不过,我已经在 UIView 中使用了这段代码。您应该能够对 scrollview 进行这些调整。

    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()
}
}

不要忘记在正确的地方删除通知。

func removeKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

Swift 4

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)
}

为了防止有人在 斯威夫特中寻找解决方案,在代码中加入以下内容:

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
}
}

Swift 5

丹尼尔 · 克罗姆(Daniel Krom)更新的答案如下:

extension UIView {


func bindToKeyboard() {
NotificationCenter.default.addObserver(
self,
selector: #selector(UIView.keyboardWillChange(notification:)),
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil
)
}


func unbindToKeyboard() {
NotificationCenter.default.removeObserver(
self,
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil
)
}


@objc func keyboardWillChange(notification: Notification) {
let duration = notification.userInfo![UIResponder.keyboardAnimationDurationUserInfoKey] as! Double
let curve = notification.userInfo![UIResponder.keyboardAnimationCurveUserInfoKey] as! UInt
let curFrame = (notification.userInfo![UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let targetFrame = (notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let deltaY = targetFrame.origin.y - curFrame.origin.y


UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIView.KeyframeAnimationOptions(rawValue: curve), animations: {
self.frame.origin.y += deltaY
})
}


}