2

I have a UICollectionView which displays cells that, in part, have images I need to get from a server. In cellForItemAt, I check my cache to see if the image is available and if not I call a method to download the image.

In that method, I load the image asynchronously. When the image is downloaded, I check to see if the indexPath associated with that image is visible. If so I call reloadItems to update the display.

The problem is that I can see the empty cell on the simulator, but it is not in the array of visible cells.

Here is a minimal snippet that shows the problem.

   func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell: ThumbCell = collectionView.dequeueReusableCell(withReuseIdentifier: kReuseId, for: indexPath) as! PosterThumbCell


    print("cellforItemAt: \(indexPath)")
    print("visibleItems: \(collectionView.indexPathsForVisibleItems)")

    ...
    return cell
}

Now I would expect the indexPath to be in the array of visible items. But it is not. Is there some event that has to occur before the item is considered visible? What am I missing?

Lukas Würzburger
  • 6,543
  • 7
  • 41
  • 75
dar512
  • 862
  • 8
  • 18

1 Answers1

2

collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell is called by the collectionView to get a cell it will then display. Therefore, if you print the visible cells before you return from this function, your newly dequeued cell won't be in the array.

To test this, add a test button and move your print from this datasource function to the button's handler. Tap the button after your cell is displayed, it will be in the array.

I don't know what your download method looks like but your skeleton should like like this :

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell: ThumbCell = collectionView.dequeueReusableCell(withReuseIdentifier: kReuseId, for: indexPath) as! PosterThumbCell


    if let image = self.getImage(for: indexPath) {
        cell.image = image
    } else {
        weak var weakCell = cell
        self.loadImageFromServer(for: indexPath) { (image) in
            // Should check that the cell is still used for the same IndexPath
            weakCell?.image = image
        }    
    }
    return cell
}

Assign image if already downloaded, else download it and assign it on completion. Please note that the cell you use when starting the request can be reused for an other indexPath when the download ends. For more details you should have a look to this answer.

Community
  • 1
  • 1
tgyhlsb
  • 1,905
  • 14
  • 21