diff --git a/Sources/Controllers/MessagesViewController+Keyboard.swift b/Sources/Controllers/MessagesViewController+Keyboard.swift index 4b50d937..2abbd030 100644 --- a/Sources/Controllers/MessagesViewController+Keyboard.swift +++ b/Sources/Controllers/MessagesViewController+Keyboard.swift @@ -45,9 +45,15 @@ internal extension MessagesViewController { @objc private func handleTextViewDidBeginEditing(_ notification: Notification) { - if scrollsToBottomOnKeyboardBeginsEditing { - guard let inputTextView = notification.object as? InputTextView, inputTextView === messageInputBar.inputTextView else { return } - messagesCollectionView.scrollToBottom(animated: true) + if scrollsToLastItemOnKeyboardBeginsEditing || scrollsToBottomOnKeyboardBeginsEditing { + guard let inputTextView = notification.object as? InputTextView, + inputTextView === messageInputBar.inputTextView else { return } + + if scrollsToLastItemOnKeyboardBeginsEditing { + messagesCollectionView.scrollToLastItem() + } else { + messagesCollectionView.scrollToBottom(animated: true) + } } } diff --git a/Sources/Controllers/MessagesViewController.swift b/Sources/Controllers/MessagesViewController.swift index bc4bc00e..982a2df5 100644 --- a/Sources/Controllers/MessagesViewController.swift +++ b/Sources/Controllers/MessagesViewController.swift @@ -36,10 +36,18 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource { /// The `InputBarAccessoryView` used as the `inputAccessoryView` in the view controller. open lazy var messageInputBar = InputBarAccessoryView() + /// A Boolean value that determines whether the `MessagesCollectionView` scrolls to the + /// last item whenever the `InputTextView` begins editing. + /// + /// The default value of this property is `false`. + /// NOTE: This calls scrollToLastItem where as the below flag calls scrollToBottome - check methods for differences + open var scrollsToLastItemOnKeyboardBeginsEditing: Bool = false + /// A Boolean value that determines whether the `MessagesCollectionView` scrolls to the /// bottom whenever the `InputTextView` begins editing. /// /// The default value of this property is `false`. + /// NOTE: This calls scrollToBottome where as the above flag calls scrollToLastItem - check methods for differences open var scrollsToBottomOnKeyboardBeginsEditing: Bool = false /// A Boolean value that determines whether the `MessagesCollectionView` diff --git a/Sources/Views/MessagesCollectionView.swift b/Sources/Views/MessagesCollectionView.swift index 56cc313a..ce960897 100644 --- a/Sources/Views/MessagesCollectionView.swift +++ b/Sources/Views/MessagesCollectionView.swift @@ -100,8 +100,24 @@ open class MessagesCollectionView: UICollectionView { cell?.handleTapGesture(gesture) } + // NOTE: It's possible for small content size this wouldn't work - https://github.com/MessageKit/MessageKit/issues/725 + public func scrollToLastItem(at pos: UICollectionView.ScrollPosition = .bottom, animated: Bool = true) { + guard numberOfSections > 0 else { return } + + let lastSection = numberOfSections - 1 + let lastItem = numberOfItems(inSection: lastSection) - 1 + + guard lastItem >= 0 else { return } + + let indexPath = IndexPath(row: lastItem, section: lastSection) + scrollToItem(at: indexPath, at: pos, animated: animated) + } + + // NOTE: This method seems to cause crash in certain cases - https://github.com/MessageKit/MessageKit/issues/725 + // Could try using `scrollToLastItem` above public func scrollToBottom(animated: Bool = false) { - performBatchUpdates(nil) { _ in + performBatchUpdates(nil) { [weak self] _ in + guard let self = self else { return } let collectionViewContentHeight = self.collectionViewLayout.collectionViewContentSize.height self.scrollRectToVisible(CGRect(0.0, collectionViewContentHeight - 1.0, 1.0, 1.0), animated: animated) }