Lance Samaria
Lance Samaria

Reputation: 19572

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

This isn't a question about raising the collectionView when the keyboard rises and lowers, that part works fine. This is about the scrollView inside the collectionView not rising with it.

I have a view pinned to the bottom of the screen and inside that view I have a textView. Pinned to the top of the view is a collectionView that's pinned to the top of the screen

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

When the textView's textField is tapped I use a notification which detects when the keyboard rises and lowers. It works fine because the collectionView goes up and down like it's supposed to. The problem is the collectionView's contents. If the collectionView's cells fill the screen when the collectionView rises up it's scrollVIew doesn't raise with it so the the cells are behind the keyboard. 2 pics below.

When the keyboard notification is sent I tried changing the collectionView's contentInset, scrollIndicatorInsets, and tried scrolling to the last cell but nothing. The cells just scroll up a tad bit.

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

Before the keyboard rises enter image description here

After the keyboard rises the collectionView is up but it's contents aren't enter image description here

Upvotes: 3

Views: 4326

Answers (2)

Wes Chua
Wes Chua

Reputation: 1076

Changing to keyboardDidShow might solve the problem because keyboardWillShow is called when the keyboard is showing up.

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

Adding the function will help to scroll the view to the bottom.

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

Upvotes: 0

Andrey Solera
Andrey Solera

Reputation: 2402

This should be the accepted answer:

Add the listener when the keyboard is raised:

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

Then add this function:

@objc func handleKeyboardWillShow(notification: Notification) {

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

Note: If you have more than one section, change the section value

Upvotes: 4

Related Questions