Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keeping CollectionView at bottom when inserting any new message. #10

Closed
tareksabry1337 opened this issue Feb 25, 2021 · 10 comments
Closed
Labels
question Further information is requested

Comments

@tareksabry1337
Copy link

First off, Hats off for this amazing layout, I have always struggled with chat layouts due to weird bugs and unknown issues that happen during the development, but this layout has made my life 100x easier.

What I'm trying to achieve is to always keep the CollectionView at bottom when a new message is inserted.

I tried this code snippet

                self.collectionView.reload(using: stagedChangeSet) { messages in
                    self.messages = messages
                    self.collectionView.scrollToItem(at: IndexPath(item: messages.count - 1, section: 0), at: .bottom, animated: true)
                }

But it looks like it's not working and throwing this error

[UICollectionView] Warning: Invalid IndexPath <NSIndexPath: 0x9ece669de5d637ba> {length = 2, path = 0 - 6} specified - will use a contentOffset of {0,0} as a fallback value. <UICollectionView: 0x7fb7fd072400; frame = (0 0; 375 529); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x600001007720>; layer = <CALayer: 0x6000018633a0>; contentOffset: {0, 0}; contentSize: {374.99990000000003, 1412}; adjustedContentInset: {0, 0, 0, 0}; layout: <ChatLayout: 0x7fb7fcc25140>; dataSource: <ChatBotViewController: 0x7fb7faf35b20>>

Any idea what I'm doing wrong ?

@ekazaev
Copy link
Owner

ekazaev commented Feb 28, 2021

@tareksabry1337 Hi. Thank you very much.
Back to your issue. The example app keeps the layout at the bottom without any scrolling required. What exactly are you trying to achieve? Are yoiu trying to scroll to the bottom when a new message appears even if the user is not at the bottom of the conversation?
Also, you can not do scrollToItem at that place because insertion animation is in progress and that index path does not exists at that moment.

@ekazaev ekazaev added the question Further information is requested label Feb 28, 2021
@tareksabry1337
Copy link
Author

Yes I'm trying to scroll to the bottom when a new message appears even if the user is not at the bottom of the conversation. I achieved "keeping at bottom" always by using keepContentOffsetAtBottomOnBatchUpdates but I'd like to keep it always at bottom no matter what.

@ekazaev
Copy link
Owner

ekazaev commented Mar 1, 2021

@tareksabry1337 . Thank you. I see. keepContentOffsetAtBottomOnBatchUpdates is the flag that changes the behaviour of the scrolling. But not every batch update is a "new message". You may reload cell or delete a cell. Or typing indicator or read/delivered labels are not exactly a "new message". So you need to detect when exactly you want to scroll to the bottom and use self.collectionView.scrollToItem(at: IndexPath(item: messages.count - 1, section: 0), at: .bottom, animated: true) after the the batch process is finished. Like in completion method. You can not do that during the batch update itself. That is the reason why you are getting that warning. That index path was not physically inserted yet.

@ekazaev
Copy link
Owner

ekazaev commented Mar 1, 2021

As a demo you can achieve it in the example app by adding self.collectionView.scrollToItem(at: IndexPath(item: sections.first!.elements.count - 1, section: 0), at: .bottom, animated: true) to the completion block at func process().
lastmessage

@ekazaev
Copy link
Owner

ekazaev commented Mar 1, 2021

@tareksabry1337 Keep in mind that scrollToItem(at: does not include the desired offset from the bottom and it also just calculates the offset of the item at the beginning of the animation, so if there are some uncalculated items in between it may not scroll you exactly to the desired place. So better try to use restoreContentOffset(with:)

@ekazaev ekazaev closed this as completed Mar 1, 2021
@tareksabry1337
Copy link
Author

I was able to achieve it using

                self.collectionView.reload(using: stagedChangeSet, completion: { [weak self] _ in
                    guard let self = self else { return }
                    let positionSnapshot = ChatLayoutPositionSnapshot(indexPath: IndexPath(item: self.messages.count - 1, section: 0), kind: .cell, edge: .bottom)
                    self.layout.restoreContentOffset(with: positionSnapshot)
                }, setData: { messages in
                    self.messages = messages
                })

But my question here, is it possible to animate "restoreContentOffset" ?

Thanks

@ekazaev ekazaev reopened this Mar 1, 2021
@ekazaev
Copy link
Owner

ekazaev commented Mar 1, 2021

@tareksabry1337 Of course, use UIView.animate(withDuration:...):

                                      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
                                          completion?()
                                      })

restorecontent

@ekazaev
Copy link
Owner

ekazaev commented Mar 1, 2021

@tareksabry1337 Just keep in mind that you should not do batch updates during the animation as it can lead to the UI glitches

@ekazaev ekazaev closed this as completed Mar 1, 2021
@tareksabry1337
Copy link
Author

This worked perfectly, Thanks for your effort !

@ekazaev
Copy link
Owner

ekazaev commented Mar 1, 2021

@tareksabry1337 My pleasure. Good luck

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants