iOS11出现键盘时UITableView将键盘高度添加到contentSize

Posted

技术标签:

【中文标题】iOS11出现键盘时UITableView将键盘高度添加到contentSize【英文标题】:UITableView adds height of keyboard to contentSize when the keyboard appears on iOS11 【发布时间】:2019-02-26 10:58:32 【问题描述】:

我正在开发一个应该在 ios 11 和 12 上运行的聊天。在 iOS 12 上,一切都按预期运行。但是,在 iOS 11 上,我遇到的问题是,一旦键盘出现,表格视图内容的大小就会增加(没有单元格)。额外高度的数量与键盘高度匹配。

演示

这是一个演示,左侧是 iOS 11,右侧是 iOS 12。在 iOS 12 上一切正常。当键盘出现时,请注意 iOS 11 上 table view 的底部。

视图/视图控制器层次结构设置

- = 视图控制器+ = 视图

- UINavigationViewController
    - UIViewController // Controlling contentInsets, contentOffset of the tableView
        + UIView
        - UITableViewController
            + UITableView
        - UIViewController // Controlling the text input bar at the bottom
            + ... // Other views
            + UITextView

布局约束

表格视图的锚点等于其父视图的锚点。所以全屏,忽略安全区域。因此,当键盘出现时,框架不会改变,但底部的内容会插入。

更多详情

我已经设置tableView.contentInsetAdjustmentBehavior = .never

这是我在键盘出现时计算表格视图的插入和偏移的方式。这很复杂,因为有几种情况应该有不同的行为。当键盘消失以及文本输入的高度发生变化时,也会进行类似的复杂计算。我总是想根据视图框架的变化向上或向下滚动表格视图。

@objc func handleKeyboardWillShowNotification(_ notification: NSNotification) 
    let frameEnd: CGRect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as AnyObject).cgRectValue ?? .zero
    let keyboardHeight = frameEnd.height
    let contentHeight = tableView.contentSize.height
    let visibleTableViewHeight = tableView.frame.height - (tableView.contentInset.top + tableView.contentInset.bottom)
    let distanceToScroll = (keyboardHeight - view.safeAreaInsets.bottom)
    var y: CGFloat = 0
    if contentHeight > visibleTableViewHeight 
        y = tableView.contentOffset.y + distanceToScroll
     else 
        let diff = visibleTableViewHeight - contentHeight
        let positionAtKeyboard = distanceToScroll - tableView.contentInset.top - diff
        y = positionAtKeyboard < tableView.contentInset.top ? -tableView.contentInset.top : positionAtKeyboard
    
    let contentOffset = CGPoint(x: 0, y: y)
    tableView.contentInset.bottom = keyboardHeight + inputBar.frame.height
    tableView.scrollIndicatorInsets = tableView.contentInset
    tableView.setContentOffset(contentOffset, animated: false)

我也在不同的屏幕尺寸上试过这个,它总是给contentSize增加一个与键盘高度完全匹配的数量。

【问题讨论】:

不确定 iOS 版本对此有何影响,但作为替代方案,您可以使用 UITableViewController 作为主控制器。这内置了对键盘避免滚动的支持,您无需手动执行任何操作。 ios 11 及以上版本不要使用 UIResponder.keyboardDidShowNotification 观察者,而是使用 UIResponder.keyboardWillChangeFrameNotification 【参考方案1】:

您可以使用以下代码进行键盘隐藏和显示。

//显示键盘。

@objc func keyboardWillAppear(_ notification: NSNotification) 
    if let newFrame = (notification.userInfo?[ UIResponder.keyboardFrameEndUserInfoKey ] as? NSValue)?.cgRectValue 
        if self.tableView.contentInset.bottom == 0 
            let insets: UIEdgeInsets = UIEdgeInsets( top: 0, left: 0, bottom: newFrame.height, right: 0 )
            self.tableView.contentInset = insets
            self.tableView.scrollIndicatorInsets = insets
            UIView.animate(withDuration: 0.1) 
                self.view.layoutIfNeeded()
            
        
    

//隐藏键盘。

@objc func keyboardWillDisappear(_ notification: NSNotification) 
    if self.tableView.contentInset.bottom != 0 
        self.tableView.contentInset = UIEdgeInsets( top: 0, left: 0, bottom: 0, right: 0 )
        self.tableView.scrollIndicatorInsets = UIEdgeInsets( top: 0, left: 0, bottom: 0, right: 0 )
        UIView.animate(withDuration: 0.1) 
            self.view.layoutIfNeeded()
        
    

这对我有用。

【讨论】:

是的,我确实在倒数第三行设置了scrollIndicatorInsets @LukasWürzburger 你能用这段代码检查一下吗?因为上面的代码对我有用。我也遇到了同样的问题。 你真的读过这个问题吗?我需要在不同情况下的特定行为。我真的需要手动设置contentOffset【参考方案2】:

首先你不必做不必要的计算 只需计算键盘高度,然后将键盘向上移动。

Swift 版本:

@objc func keyboardWillShow(notification: NSNotification) 
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue 
        self.tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height + 10, right: 0)
        UIView.animate(withDuration: 0.25) 
            self.tableView.layoutIfNeeded()
            self.view.layoutIfNeeded()
        
    


@objc func keyboardWillHide(notification: NSNotification) 

    self.tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    UIView.animate(withDuration: 0.5) 
        self.tableView.layoutIfNeeded()
        self.view.layoutIfNeeded()
    

Objective-C 版本:

- (void)keyboardWillShow:(NSNotification *)notification

    NSDictionary *keyInfo = [notification userInfo];
    CGRect keyboardFrame = [[keyInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];

    self.tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardFrame.size.height + 10, 0);

    [UIView animateWithDuration:0.2 animations:^
        [self.tableView layoutIfNeeded];
        [self.view layoutIfNeeded];
     completion:nil];


- (void) keyboardWillHide:  (NSNotification *) notification

    self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
    [UIView animateWithDuration:0.2 animations:^
        [self.view layoutIfNeeded];
     completion:nil];

如果您发现任何困难,请告诉我。 这对我很有效

【讨论】:

【参考方案3】:

解决方法

这并不是专门回答最初的问题,但对于那些没有半透明键盘和输入视图的人来说可能是一个解决方案。

我可以通过更改约束而不设置底部插图来解决这个问题。最初,表格视图的底部约束设置为超级视图的底部(基本上是屏幕底部)。所以当键盘出现时,我并没有改变表格视图的框架,而是底部的插图。这显然不能正常工作。

现在我已将表格视图的底部约束设置为输入视图的顶部(黑条),并将底部插图设置为零。由于当键盘出现时输入视图向上移动,它改变了表格视图的框架,底部插图保持为零。我还是设置了内容偏移,因为我需要不同情况下的特定行为,仅此而已。

这仅适用于我的情况,因为我既没有半透明的输入栏也没有键盘,也不需要在其后面显示模糊的内容。

【讨论】:

以上是关于iOS11出现键盘时UITableView将键盘高度添加到contentSize的主要内容,如果未能解决你的问题,请参考以下文章

带有文本字段的 UiTableView - 出现键盘时滚动

出现键盘时,UITableView 的部分页脚正在移动

键盘出现时向上移动 UITextField 和 UITableView

iOS UITableView reloadData 导致键盘收起不能连续输入问题解决办法

iOS 7 - 键盘动画

使用自定义键盘时使 UITableView 滚动到正确的位置