Closed deehegarty closed 3 years ago
Hi @deehegarty
Thank you for the issue. Can you explain me some details. Current Example app does not scroll to the bottom of the collection view after the new message is sent. Or if it does - it is a side effect of some other action. Is that the issue within your modification?
Actual behaviour: When a user sends a message and taps on the send button, the CollectionView will scroll to show the just top of the last item.
I should give some details here.
Whatever happens with layout items outside of visible area - they will always have an estimated value. Every collection layout behaves this way. The actual size of the cell will be calculated only at the moment of appearance of the cell. Just imagine the situation when you inserted not just 1 cell but 10000, but all of them are outside of the visible area. It will take severe amount of time to calculate 10000 layouts. But this calculations are completely unnecessary. So you can not rely on collectionView.setContentOffset(WHATEVER, animated: true)
as the actual contentOffset
of the cell that you expect to reach will be different. During the animated scrolling every cell that appears will be calculated and will change the contentSize
. It applies to any collection view layout.
If you require to scroll to some specific cell - you should use func restoreContentOffset(with snapshot: ChatLayoutPositionSnapshot)
provided with ChatLayout
So, in the current example app. If you require to scroll to the bottom of the collection view every time user sends a message - it needs to be modified this way:
self.chatController.sendMessage(.text(messageText)) { sections in
UIView.animate(withDuration: 0.25, animations: {
let positionSnapshot = ChatLayoutPositionSnapshot(indexPath: IndexPath(item: 0, section: 0), kind: .footer, edge: .bottom)
self.chatLayout.restoreContentOffset(with: positionSnapshot)
}, completion: { _ in
self.currentInterfaceActions.options.remove(.sendingMessage)
self.processUpdates(with: sections, animated: true)
})
}
But, be aware: If you are using UIView.animate(...)
as suggested, you may face the issues like one described here https://dasdom.dev/posts/scrolling-a-collection-view-with-custom-duration/ and you should implement your own animated scrolling method using CADisplayLink
if you want everything to be smooth.
@deehegarty Please let me know if you are satisfied with the answer. If you have any other question - please do not hesitate to ask.
@ekazaev Thanks so much for this information (some really good info here). What I had inside public func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith text: String)
was a call to scrollToBottom()
instead of the code snippet you have just provided. I had thought that calling scrollToBottom()
at this point would scroll to the last item in the CollectionView. What actually happens is the behaviour that I have described above.
@deehegarty I think its just a misleading name of the method. I use it only to adjust the offset the the size of input view changes, it usually happens within the visible zone, so for me those nuances are not critical. Ill update the Example app to avoid the confusion.
@ekazaev Thanks so much for the clarification 😃
@deehegarty JFYI: The example app was recently updated to demonstrate one of the possible implementations of your request
Expected behaviour: When a user sends a message and taps on the send button, the should be a scroll to the last item in the CollectionView.
Actual behaviour: When a user sends a message and taps on the send button, the CollectionView will scroll to show the just top of the last item.
It appears to be taking the estimated item size instead of the actual size of the new cell that has just been sent by the end user. The cell is added to the view and can be seen once you manually scroll to the bottom. Example: when we increased the estimated item height to 100, the scrolling was correct, but only for smaller messages. If it was a bigger message, it would be cut off after 100 height.
Something to note is that the above behaviour occurs when
animation = true
when scrolling i.e.self?.collectionView.setContentOffset(contentOffsetAtBottom, animated: true)
. Whenanimation = false
the CollectionView will scroll to the correct position.Reproduction steps:
Note:
scrollToBottom()
is not currently working in the Example app provided