MJQZ1347 MJQZ1347 - 4 months ago 35
Swift Question

How to detect if last cell is visible in UICollectionView?

I am trying to detect if the last cell in a

collectionView
is visible.

var isLastCellVisible: Bool {

let lastIndexPath = NSIndexPath(forItem: self.messages.count - 1, inSection: 0)
let visibleIndexPaths = self.collectionView.indexPathsForVisibleItems()

let lastCellPositionY = self.collectionView.layoutAttributesForItemAtIndexPath(lastIndexPath)!.frame.origin.y
let bottomInset = self.collectionView.contentInset.bottom // changes when the keyboard is shown / hidden
let contentHeight = self.collectionView.contentSize.height

if visibleIndexPaths.contains(lastIndexPath) && (contentHeight - lastCellPositionY) > bottomInset {
return true
} else {
return false
}
}


What works:

If the last cell is visible and the keyboard is shown so that the cell is not hidden by it, the above code returns true. If it is hidden it returns false.

But I can't figure out how to return true, when the user scrolls up and the last cell is above the keyboard.

lastCellPositionY
and
contentHeight
don't change when the
collectionView
gets scrolled up.
Instead
self.collectionView.bounds.origin.y
does change, but I don't know how to compare
lastCellPositionY
to it, since they don't share the same origin and
self.collectionView.bounds.origin.y
is significantly less than
lastCellPositionY
.

Answer

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
}
Comments