How to detect if last cell is visible in UICollectionView?
I use this code (translated to Swift from the commits in this thread: https://github.com/jessesquires/JSQMessagesViewController/issues/1458)
var isLastCellVisible: Bool {
if self.messages.isEmpty {
return true
}
let lastIndexPath = NSIndexPath(forItem: self.messages.count - 1, inSection: 0)
var cellFrame = self.collectionView.layoutAttributesForItemAtIndexPath(lastIndexPath)!.frame
cellFrame.size.height = cellFrame.size.height
var cellRect = self.collectionView.convertRect(cellFrame, toView: self.collectionView.superview)
cellRect.origin.y = cellRect.origin.y - cellFrame.size.height - 100
// substract 100 to make the "visible" area of a cell bigger
var visibleRect = CGRectMake(
self.collectionView.bounds.origin.x,
self.collectionView.bounds.origin.y,
self.collectionView.bounds.size.width,
self.collectionView.bounds.size.height - self.collectionView.contentInset.bottom
)
visibleRect = self.collectionView.convertRect(visibleRect, toView: self.collectionView.superview)
if CGRectContainsRect(visibleRect, cellRect) {
return true
}
return false
}
What i am using
override func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == dataSource.count - 1 {
// Last cell is visible
}
}
An handy extension with two computed properties for this.
extension UICollectionView {
var isLastItemFullyVisible: Bool {
let numberOfItems = numberOfItems(inSection: 0)
let lastIndexPath = IndexPath(item: numberOfItems - 1, section: 0)
guard let attrs = collectionViewLayout.layoutAttributesForItem(at: lastIndexPath)
else {
return false
}
return bounds.contains(attrs.frame)
}
// If you don't care if it's fully visible, as long as some of it is visible
var isLastItemVisible: Bool {
let numberOfItems = collectionView.numberOfItems(inSection: 0)
return indexPathsForVisibleItems.contains(where: { $0.item == numberOfItems - 1 })
}
}
There's an alternative to the isLastItemFullyVisible. A one liner.
var isLastItemFullyVisible: Bool {
contentSize.width == contentOffset.x + frame.width - contentInset.right
}