UICollectionViewDiffableDataSource apply snapshot calls willDisplay for all cells

I have some code which currently assumes that UICollectionViewDelegate - collectionView(_: UICollectionView, willDisplay _: UICollectionViewCell, forItemAt indexPath: IndexPath) will be called as the user scrolls through the collection view and cells are about to be displayed on-screen.

However on refactoring this screen to use the UICollectionViewDiffableDataSource APIs I noticed that willDisplay is called for every indexPath in the collection view's data as a result of calling UICollectionViewDiffableDataSource apply`.

This is reproducible in Apple's sample app Updating Collection Views Using Diffable Data Sources running on iOS 15.

Is this behaviour intentional?

Is there another reliable way to be informed of when a cell is about to be displayed as the user is scrolling through the collection view?

As a workaround I am using a boolean flag to indicate when the data source is updating and setting that to false in the completion of UICollectionViewDiffableDataSource apply.

Many thanks!

Replies

The willDisplay callback is made for each cell before it is displayed in the collection view.

If you are applying a snapshot to diffable data source with animatingDifferences: true, and the animations require creating and displaying cells, then you will get a willDisplay callback for each cell that is becoming visible — even if the cells are created for the duration of the animation only, and end up no longer being visible at the end of the animation (in which case, you will receive a didEndDisplaying callback for each of those cells at the end of the animation).

Generally speaking, when you are initially populating data, you should use animatingDifferences: false (or applySnapshotUsingReloadData(_:)) to avoid unnecessary animations, which can require many more cells to get created just for the animation than the total number that actually end up visible at the end (depending on the type of animation for the inserted cells).

Thank you for your response. But I'm afraid what I'm observing contradicts what you've said. The sample app for Updating Collection Views Using Diffable Data Sources  is already using applySnapshotUsingReloadData and yet willDisplay is called for all cells even those not on-screen.

I had to add a number of extra entries into the recipeData.json within that sample project in order to have more cells than can be rendered on-screen. But after doing so and adding a print statement in willDisplay I observed that it was being called for all cells (including those not on-screen). The same is true using for animatingDifferences: false (observed on both iOS 14 and iOS 15).

Should I file a bug report if this is not expected behaviour?

Additionally we have observed that willDisplayCell gets called for every cell upon device rotation without any calls to updating the snapshot.

I am seeing the same problem as well. I tried calling applySnapshotUsingReloadData() and apply(_,animatingDifferences: false). This causes me grief since we use the willDisplay as a trigger to fetch more data from our servers.

This worked fine using traditional DataSource delegate for year.

Hey,

Did you manage to find a resolution for this or did you just continue with the bool check on the datasource loading state?

I'm having the same issue with iOS 15 but haven't found a sensible way around it. Also, did you submit this as a bug to Apple?