cellForItemAtIndexPath returns nil after force scrolling to make it visible
Solution 1
Based on your comments, it sounds like what you're ultimately after is the cell's frame. The way to do that without relying on existence of the cell is to ask the collection view's layout:
NSIndexPath *indexPath = ...;
UICollectionViewLayoutAttributes *pose = [self.collectionView.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath];
CGRect frame = pose.frame;
Solution 2
I figured out a solution for now. If I call [myView layoutIfNeeded]
right after my call to reloadData, but before my attempt to retrieve the cell everything works fine. Right now all the cells are cached so access is fast, but I am scared this might give me bad performance if I have to load from the web or an internal database, but we'll see.
Solution 3
If you want to access the cell and have it visible on screen:
NSIndexPath *indexPath = ...;
[collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically | UICollectionViewScrollPositionCenteredHorizontally animated:NO];
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
if(cell == nil) {
[collectionView layoutIfNeeded];
cell = [collectionView cellForItemAtIndexPath:indexPath];
}
if(cell == nil) {
[collectionView reloadData];
[collectionView layoutIfNeeded];
cell = [collectionView cellForItemAtIndexPath:indexPath];
}
I very rarely enter the second if statement, but having two fallbacks like this works very well.
Solution 4
hfossli's solution worked for me, so I've provided a Swift version below. In my case, I was unable to get a reference to a custom UICollectionViewCell, probably because it was not visible. I tried many things, but just as Shaybc said, this was the sole working solution.
Swift 3:
func getCell(_ indexPath: IndexPath) -> CustCell? {
cView.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition.centeredHorizontally, animated: false)
var cell = cView.cellForItem(at: indexPath) as? CustCell
if cell == nil {
cView.layoutIfNeeded()
cell = cView.cellForItem(at: indexPath) as? CustCell
}
if cell == nil {
cView.reloadData()
cView.layoutIfNeeded()
cell = cView.cellForItem(at: indexPath) as? CustCell
}
return cell
}
usage:
let indexPath = IndexPath(item: index, section: 0)
cView.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition(), animated: true)
if let cell = getCell(indexPath) {
cell. <<do what you need here>>
}
Related videos on Youtube
Kevin DiTraglia
Updated on September 15, 2022Comments
-
Kevin DiTraglia over 1 year
So I am trying to write some code that scrolls a collection view to a certain index, then pulls a reference to the cell and does some logic. However I've noticed if that cell wasn't presently visible prior to the scroll, the
cellForItemAtIndexPath
call will return nil, causing the rest of my logic to fail.[_myView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0] atScrollPosition:UICollectionViewScrollPositionTop animated:NO]; //Tried with and without this line, thinking maybe this would trigger a redraw [_myView reloadData]; //returns nil if cell was off-screen before scroll UICollectionViewCell *cell = [_myView cellForItemAtIndexPath: [NSIndexPath indexPathForItem:index inSection:0]];
Is there some other method I have to call to cause the
cellForItemAtIndexPath
to return something for a cell that suddenly came into view as a result of the scroll immediately preceding it?-
Kevin DiTragliaI actually just want the location of a cell because I'm animating something else to that location during a transition. So what I need isn't really a part of the datasource, but a part of where it is going to be drawn in the screen.
-
Aaron BragerI think there's a problem in your design if you need a reference to your cell. You should be able to update your data model with your changes, scroll to the cell, and what you want should appear. You shouldn't need to then get the cell and modify it.
-
-
Kevin DiTraglia about 10 yearsAh very cool. The only problem now is I actually animate to the location of a subview in the cell, which I am unable to obtain with this method. I could kind of fudge it since it's always in the same spot, because I imagine this has some performance benefits from how I am doing it.
-
Kevin DiTraglia almost 10 yearsRevisiting this months later, this is now the solution, so giving you the super late checkmark.
-
RPM about 8 yearsThanks this was very helpful !
-
Shaybc about 8 yearsI searched long time to force collectionView to display a certain cell as focused and selected and your method (as expensive as it looks) is the sole working solution, thanks for investing the time to suggest it,
-
jaredsinclair almost 8 yearsVerified this works for me, too. I think it's because AutoLayout passes aren't run every time one might expect them to.