Recently a user reported a bug:
Some pictures don't show if I scroll up immediately after scrolling down in a user album.
Fortunately I can reproduce it. After several hours' debugging I finally found the cause:
cellForItemAtIndexPath is not called for those
UICollectionViewCells before they are displayed, i.e. before
willDisplayCell is called.
The missing of
cellForItemAtIndexPath calls doesn't occur usually, of course. Otherwise, all apps using
UICollectionView will break.
It only occurs for those cells that disappear and then immediately appear again—the user scrolls them out of bounds and then scrolls them back into bounds again.
cellForItemAtIndexPath is correctly called when they first appear.
Though this behavior was unexpected to me—that's why bugs happen—it is totally reasonable:
cellForItemAtIndexPath is for binding data to cell. If a cell's
indexPath doesn't change its corresponding data doesn't change—assuming the data source doesn't change—it can be reused as is without data rebinding.
Most apps won't be affected by this behavior because they only do data binding not data unbinding. But my app does data unbinding, out of my excessive care about memory usage.
The place I do data unbinding is
didEndDisplayingCell. Without the expected
cellForItemAtIndexPath call those cells don't have the chance to rebind to their data, hence show up empty.
Though this behavior currently only occurs for
UICollectionView. You should also take it into account when using
willDisplayCellmay be called without calling
cellForRowAtIndexPathfirst. The cell may be scrolled back into bounds immediately after being scrolled out of bounds while user is scrolling back and forth. In this situation
willDisplayCellmay be called after
Data unbinding can be safely done in
didEndDisplayingCellonly if data binding is done in
willDisplayCell. But normally data binding is done in
cellForRowAtIndexPathso there is no safe and convenient place to do data unbinding.