Swift - 如何在键盘抬起时滚动 collectionView 的内容

Swift -How to scroll the collectionView's contents when the keyboard is raised

这不是关于在键盘升高和降低时升高 collectionView 的问题,这部分工作正常。这是关于collectionView里面的scrollView没有跟着上升的问题

我有一个固定在屏幕底部的视图,在该视图中我有一个 textView。固定到视图顶部的是固定到屏幕顶部的 collectionView

-top of viewController
    collectionView
    containerView that contains a textView
-bottom of viewController

当轻触 textView 的 textField 时,我使用一个通知来检测键盘何时上升和下降。它工作正常,因为 collectionView 像预期的那样上下移动。问题是 collectionView 的内容。如果 collectionView 的单元格在 collectionView 上升时填满屏幕,则 scrollVIew 不会随之上升,因此单元格位于键盘后面。下面2张图片。

发送键盘通知时,我尝试更改 collectionView 的 contentInset、scrollIndicatorInsets,并尝试滚动到最后一个单元格,但没有任何效果。单元格只是向上滚动了一点点。

@objc fileprivate func keyboardWillShow(notification: Notification) {

    guard let keyboardDuration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double else { return }
    guard let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }

    let keyboardFrame = keyboardFrame.cgRectValue
    let keyboardHeight = keyboardFrame.height

    containerViewBottomAnchorConstraint?.isActive = false
    containerViewBottomAnchorConstraint = containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -keyboardHeight)
    containerViewBottomAnchorConstraint?.isActive = true

    // I tried this
    let item = tableData.count - 1
    let indexPath = IndexPath(item: item, section: 0)
    if !tableData.isEmpty {
        collectionView.scrollToItem(at: indexPath, at: .bottom, animated: true)
    }

    // I tried this
    let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: -keyboardHeight, right: 0)
    collectionView.contentInset = contentInsets
    collectionView.scrollIndicatorInsets = contentInsets

    UIView.animate(withDuration: keyboardDuration, animations: {
        self.view.layoutIfNeeded()
    }) { [weak self](_) in
        self?.autoScrollToLastCell() // I tried this
    }
}

@objc fileprivate func keyboardWillHide(notification: Notification) {

    guard let keyboardDuration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double else { return }

    containerViewBottomAnchorConstraint?.isActive = false
    containerViewBottomAnchorConstraint = containerView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0)
    containerViewBottomAnchorConstraint?.isActive = true

    UIView.animate(withDuration: keyboardDuration, animations: {
        self.view.layoutIfNeeded()
    }) { [weak self](_) in
        self?.autoScrollToLastCell()
    }
}

func autoScrollToLastCell() {
    let section = 0
    let item = collectionView.numberOfItems(inSection: section) - 1
    let index = IndexPath(item: item, section: section)
    if item > 0 {
        collectionView.scrollToItem(at: index, at: .bottom, animated: true)
    }
}

键盘升起前

键盘升起后,collectionView 已启动,但其内容未启动

修复很简单,我所要做的就是将 collectionView 的内容插入设置为 UIEdgeInsets(top: 0, left: 0, bottom: 8, right: 0)

下面我将 bottom 参数设置为 8,这样看起来最后一个单元格的底部和 containerView 的顶部之间可以有填充

collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 8, right: 0)

为清楚起见,设置 collectionView 的 contentInset 属性 是解决此问题的方法。这与8分无关。即使它是 0,0,0,0,它也能正常工作。 8分只是给appearance/preference的。这里没有使用任何“神奇数字”来解决任何问题。

这应该是公认的答案:

在键盘抬起时添加监听器:

NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)

然后添加这个函数:

@objc func handleKeyboardWillShow(notification: Notification) {

            collectionView.scrollToItem(at: IndexPath(row: messagesList.count - 1, section: chatSection), at: .top, animated: false)
    }

注意:如果您有多个部分,请更改 section

更改为 keyboardDidShow 可能会解决问题,因为 keyboardWillShow 在键盘出现时被调用。

NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow), name: UIResponder.keyboardDidShowNotification, object: nil)

添加该功能将有助于将视图滚动到底部。

@objc func keyboardDidShow() {
    self.collectionView.scrollToItem(at: [0, messageList.count - 1],
                                     at: .bottom,
                                     animated: false)
}