Reputation: 8376
I have a UICollectionView
with a chat like view - messages are displayed with the newest at the bottom. As the user scrolls up it loads previous messages and updates the collection view. I am trying to maintain the content offset of the UICollectionView
as new data is added but I can't get it to work.
Here's what I have at the moment:
// First find the top most visible cell.
if let topCellIndexPath = collectionView.indexPathsForVisibleItems.sorted().first,
let topCell = collectionView.cellForItem(at: topCellIndexPath),
let topCellLayout = collectionView.layoutAttributesForItem(at: topCellIndexPath) {
// Save the y position of the top cell.
let previousTopCellY = topCellLayout.frame.origin.y
// Perform updates on the UICollectionView without animation (ignore the fact it says adapter)
adapter.performUpdates(animated: false) { [weak self] completed in
if let strongSelf = self,
let topCellNewIndexPath = strongSelf.collectionView.indexPath(for: topCell),
let newTopCellLayout = strongSelf.collectionView.layoutAttributesForItem(at: topCellNewIndexPath) {
// Calculate difference between the previous cell y value and the current cell y value
let delta = previousTopCellY - newTopCellLayout.frame.origin.y
// Add this to the collection view content offset
strongSelf.collectionView.contentOffset.y += delta
}
}
}
This doesn't seem to work, and sometimes can't get the indexPath of the cell after the update.
EDIT Based on @Arkku's answer this works. Although there is a small flicker.
let previousContentSize = collectionView.contentSize.height
adapter.performUpdates(animated: false) { [weak self] completed in
if let strongSelf = self {
let delta = strongSelf.collectionView.contentSize.height - previousContentSize
strongSelf.collectionView.contentOffset.y += delta
}
}
Upvotes: 1
Views: 3893
Reputation: 42109
As I commented earlier, it may be better to get the delta
from contentSize
rather than origin of a specific cell. A suggestion based on your own version:
let previousContentHeight = collectionView.contentSize.height
adapter.performUpdates(animated: false) { [weak self] completed in
guard let self = self else { return }
let delta = self.collectionView.contentSize.height - previousContentHeight
self.collectionView.bounds.origin.y += delta
}
Upvotes: 3