当键盘弹出时,如何使用自动布局调整视图大小

8
我有一个视图(在这个例子中是UITableView,但这不重要),我想在键盘弹出时改变高度。在自动布局中,最好的方法是什么?当前,我的视图上有以下约束:
  • 距离父视图顶部:0
  • 距离父视图右边缘:0
  • 距离父视图左边缘:0
  • 距离父视图底部:0
  • 高等于424
我认为最快的方法是删除高度和底部间距约束,然后在代码中调整实际视图大小当keyboardDidAppear通知被调用时,但还有其他方法吗?

2
在这种情况下,更新“contentInset”属性可能比玩弄实际布局更好。 - holex
3个回答

19

我可以给你一个通用的想法,但可能需要根据你实际的项目进行重新调整。

Swift 4.2

let notificationTokenKeyboardWillAppear = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: nil) { (note) in
    guard let keyboardFrame = (note.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
    UIView.animate(withDuration: CATransaction.animationDuration(), animations: {
        self.scrollView?.contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardFrame.size.height, right: 0.0)
    }, completion: nil)
}

let notificationTokenKeyboardWillHide = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: nil) { (_) in
    UIView.animate(withDuration: CATransaction.animationDuration(), animations: {
        self.scrollView?.contentInset = .zero
    }, completion: nil)
}

注意1:scrollView 在此表示任何 UIScrollView 的子集,例如UITableViewUICollectionView 等。

注意2:当您要释放视图且基于闭包的观察者不再需要时,您需要通过调用 removeObserver(_:) 方法手动删除令牌。


ObjC

我假设 UITableView *_tableView 已经在其他地方正确设置好了。

- (void)viewDidLoad {

    // ...

    [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillShowNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
        id _obj = [note.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey];
        CGRect _keyboardFrame = CGRectNull;
        if ([_obj respondsToSelector:@selector(getValue:)]) [_obj getValue:&_keyboardFrame];
        [UIView animateWithDuration:0.25f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{
            [_tableView setContentInset:UIEdgeInsetsMake(0.f, 0.f, _keyboardFrame.size.height, 0.f)];
        } completion:nil];
    }];
    
    [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillHideNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
        [UIView animateWithDuration:0.25f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{
            [_tableView setContentInset:UIEdgeInsetsZero];
        } completion:nil];
     }];


     // ...

}

注意: 如果你的 UITableView 不是精确地位于屏幕底部,则应该在此行中调整 contentInset 的值: [_tableView setContentInset:UIEdgeInsetsMake(0.f, 0.f, _keyboardFrame.size.height, 0.f)];


非常感谢您的回答,我会尝试一下。调整具有约束条件的视图大小通常不是一个好主意吗?如果我们像这样有约束条件,使用contentInset是否更好? - Enrico Susatyo
@EnricoSusatyo,我认为仅因为键盘出现而调整视图大小太不方便了。另一方面,contentInset属性及其行为设计得非常适合此目的。 - holex
谢谢你的回答,它几乎完美地解决了问题。唯一让我不太满意的是,滚动条的一半仍然被键盘遮挡,但是表格视图内容现在已经在键盘上方可见。不过我认为这仍然是目前最好的解决方案。 - Enrico Susatyo
感谢您提供的好解决方案!我已经点赞了这个问题。 - Thanh-Nhon Nguyen
3
请设置scrollIndicatorInsets以便滚动条正确移动。 - Enrico Susatyo

2
保持您的高度限制并连接一个插座:
保持您的高度限制并将插座连接到它。
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *CS_TableView_Height;

请看本教程的示例3。

使用NSNotificationCenter捕获键盘事件来更新视图的高度:

- (void)viewWillAppear:(BOOL)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
{
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

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

你可能还想看看这个问题的被接受答案,以获取一些启发。

最终,你应该得到像这样的东西:

- (void)keyboardWillShow
{

    self.CS_TableView_Height.constant = 500;//For example

    [self.tableView setNeedsUpdateConstraints];
}

- (void)keyboardWillHide
{

    self.CS_TableView_Height.constant = 568;// iPhone5 height

    [self.tableView setNeedsUpdateConstraints];
}

  1. 基于块的通知更加方便且更好的实践。
  2. 更新 contentInset 属性也是更好的实践。
- holex
1
@holex:你能否提供一些你的解决方案的代码作为答案? :) - Thanh-Nhon Nguyen
我很快就会尝试这个,但我认为像这样指定常数作为高度并不是一个好主意。其他键盘,如日语或中文键盘,可能与英文键盘的高度不同。 - Enrico Susatyo
1
所以你只需要动态获取键盘的高度并将其减去。 - Thanh-Nhon Nguyen
@NhonNguyen,没问题,但是如果我不了解整个环境(比如这种情况),我更喜欢只提供想法。 - holex
另外,您可以将选择器更改为keyboardWillShow:和keayboardWillHide:(带有冒号),然后两种方法都可以接收NSNotification对象,在其中可以读取键盘高度,这样您就不必硬编码它。 - Rob

0
首先,您不应该添加顶部和底部约束以及高度约束。如果屏幕大小发生变化,应用程序将崩溃(除非其中一个约束具有较低的优先级,在这种情况下,它将被删除)。
其次,在您的keyboardDidAppear通知方法中,您只需将底部空间更改为superview的常量值并调用 [myView setNeedsDisplay] 编辑:在becomeFirstResponder之后不需要执行setNeedsDisplay。您将 self 作为 keyboardWillShow / keyboardWillHide 通知的观察者,并在该方法中更新约束并调用 setNeedsDisplay 。
请参阅this apple post Listing 5-1 处理键盘通知和 Listing 5-2 跟踪活动文本字段的其他方法提供代码。

嘿,感谢Zsolt大人,但是看起来在我调用之后视图仍然没有重新调整大小。我已经删除了高度约束。我在调用becomeFirstResponder后立即调用了setNeedsDisplay以弹出键盘。 - Enrico Susatyo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接