当键盘出现在 iOS11 上时,UITableView 将键盘高度添加到 contentSize

UITableView adds height of keyboard to contentSize when the keyboard appears on iOS11

我正在处理应该在 iOS 11 和 12 上运行的聊天。在 iOS 12 上一切正常。但是,在 iOS 11 上,我遇到的问题是 table 视图内容大小在键盘出现后立即增加(无单元格)。额外高度的数量与键盘高度匹配。

演示

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

View/View 控制器层级设置

- = 查看控制器
+ = 查看

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

布局约束

table 视图的锚点与其父视图的锚点相同。所以全屏,忽略安全区域。所以当键盘出现时,框架不会改变,但底部内容会插入。

更多详情

我设置了tableView.contentInsetAdjustmentBehavior = .never

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

@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 添加一个与键盘高度完全匹配的量。

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

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

如果您发现任何困难,请告诉我。 这很适合我

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

//显示键盘。

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

这是我的工作。

解决方法

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

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

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

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