weird behaviour of UITableView cell recycling

I've got a table view where I'm using [UITableView dequeueReusableCellWithIdentifier:forIndexPath:] to obtain cells.

In my test scenario table is static (I do not scroll no change to number of rows etc), but I periodically call reloadData. One of cells is partially off-screen (entire contents of the table view extends beyond height of container view).

I'd expect that in such scenario, when I try to dequeue a cell for the same index path and same identifier, I will get the same instance that was used previously, but I noticed that table view tends to return cell previously used at different index path (essentially swapping the cells). Is it an expected, desired behaviour?

Accepted Reply

UITableView and UICollectionView do not make any guarantees about how cells stored in the reuse pool will be dequeued. When you dequeue a cell, a random cell from the reuse pool for the specific reuse identifier specified will be selected and returned.

Once cells end displaying and get put into the reuse pool, there is no attempt to keep them associated with a particular row/item or index path. Cells will always receive prepareForReuse when they are dequeued, regardless of whether the cell is being used for the same row/item or not, and you are expected to fully configure the cell for the new row/item it is being displayed for.

If you really need to keep different types of cells separated in the reuse pool, then you should use separate reuse identifiers for those cells.

Finally, remember that when you call reloadData, everything is reset and the UITableView/UICollectionView has no idea whether any of the old rows/items still exist at all anymore. So you should definitely not expect any cell-specific state to persist across a call to reloadData. This is one reason why it's best to avoid reloadData and instead perform batch updates, preferably using diffable data source, as that allows more fine-grained updates.

Replies

I'd expect that in such scenario, when I try to dequeue a cell for the same index path and same identifier, I will get the same instance that was used previously, but I noticed that table view tends to return cell previously used at different index path (essentially swapping the cells)

You should not expect any specific behaviour which is not documented as such.

So, you have to dequeue and rebuild (cellForRowAt) from the data source. Never expect the cells to be a data storage, they are just for display.

UITableView and UICollectionView do not make any guarantees about how cells stored in the reuse pool will be dequeued. When you dequeue a cell, a random cell from the reuse pool for the specific reuse identifier specified will be selected and returned.

Once cells end displaying and get put into the reuse pool, there is no attempt to keep them associated with a particular row/item or index path. Cells will always receive prepareForReuse when they are dequeued, regardless of whether the cell is being used for the same row/item or not, and you are expected to fully configure the cell for the new row/item it is being displayed for.

If you really need to keep different types of cells separated in the reuse pool, then you should use separate reuse identifiers for those cells.

Finally, remember that when you call reloadData, everything is reset and the UITableView/UICollectionView has no idea whether any of the old rows/items still exist at all anymore. So you should definitely not expect any cell-specific state to persist across a call to reloadData. This is one reason why it's best to avoid reloadData and instead perform batch updates, preferably using diffable data source, as that allows more fine-grained updates.