Check whether cell at indexPath is visible on screen UICollectionView
Solution 1
Get current available cells
// get visible cells
let visibleIndexPaths = followedCollectionView.indexPathsForVisibleItems
Then check if your indexPath
is contained in the visibleIndexPaths
array or not, before doing anything with cells.
Solution 2
Nested UICollectionViews need oft to not scroll at all, so that no contentOffset is ever provided and thus iOS understands all cells as being always visible. In that case it is possible to take the screen boundaries as reference:
let cellRect = cell.contentView.convert(cell.contentView.bounds, to: UIScreen.main.coordinateSpace)
if UIScreen.main.bounds.intersects(cellRect) {
print("cell is visible")
}
Solution 3
You can get current index by
// Called before the cell is displayed
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
print(indexPath.row)
}
// Called when the cell is displayed
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
print(indexPath.row)
}
Solution 4
I needed to track cells that are visible so you can also do something like this to track your cells better.
private func configureVisibleIndexPath() {
let visibleCells = collectionView.indexPathsForVisibleItems
visibleCells.forEach { indexPath in
if let cell = collectionView.cellForItem(at: indexPath), collectionView.bounds.contains(cell.frame) {
print("visible row is \(indexPath.row)"
}
}
}
Call this function when your collection view is configured properly and it also takes care on scrolling by calling it in scrollView delegates:
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
configureVisibleIndexPath()
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
configureVisibleIndexPath()
}
Solution 5
You can simply use if collectionView.cellForItem(at: indexPath) == nil { }
. The collectionView will only return a cell if it is visible.
Or in your case specifically change:
let cell = followedCollectionView.cellForItemAtIndexPath(indexPath) as! FeaturedCitiesCollectionViewCell
to:
if let cell = followedCollectionView.cellForItemAtIndexPath(indexPath) as? FeaturedCitiesCollectionViewCell { }
Alk
Updated on June 10, 2022Comments
-
Alk about 2 years
I have a
CollectionView
which displays images to the user. I download these in the background, and when the download is complete I call the following func to update thecollectionViewCell
and display the image.func handlePhotoDownloadCompletion(notification : NSNotification) { let userInfo:Dictionary<String,String!> = notification.userInfo as! Dictionary<String,String!> let id = userInfo["id"] let index = users_cities.indexOf({$0.id == id}) if index != nil { let indexPath = NSIndexPath(forRow: index!, inSection: 0) let cell = followedCollectionView.cellForItemAtIndexPath(indexPath) as! FeaturedCitiesCollectionViewCell if (users_cities[index!].image != nil) { cell.backgroundImageView.image = users_cities[index!].image! } } }
This works great if the cell is currently visible on screen, however if it is not I get a
fatal error: unexpectedly found nil while unwrapping an Optional value
error on the following line :let cell = followedCollectionView.cellForItemAtIndexPath(indexPath) as! FeaturedCitiesCollectionViewCell
Now this function does not even need to be called if the collectionViewCell is not yet visible, because in this case the image will be set in the
cellForItemAtIndexPath
method anyway.Hence my question, how can I alter this function to check whether the cell we are dealing with is currently visible or not. I know of the
collectionView.visibleCells()
however, I am not sure how to apply it here. -
Marcus over 5 yearsThis method is really good for nested UICollectionViews. Most of the time because we disable scrolling for nested UICollectionViews, visibleIndexPaths will always return all the cells even they are not visible. And this solution is the best I have found thus far online. Great one.
-
famfamfam over 2 yearsindexPathsForVisibleItems return wrong value, in my cast is must display 3,4,5,6 but it display 0,1,2,3