In my UICollectionView
, I use a simple array of custom objects to produce and display cells. Occasionally that data changes and I'd like to animate the changes all at once. I've chosen to do this by tracking all the changes in a second array, diff'ing the two, and producing a set of move, insert, delete, and update operations inside of a performBatchUpdates
block. I now realize it's pretty tricky to do all of these inside the same block because you have to worry about orders of operations with indexes. In fact, the accepted answer to this issue is wrong (but corrected in the comments).
The documentation seems pretty lacking, but it covers one case:
Deletes are processed before inserts in batch operations. This means the indexes for the deletions are processed relative to the indexes of the collection view’s state before the batch operation, and the indexes for the insertions are processed relative to the indexes of the state after all the deletions in the batch operation.
However, the document doesn't talk about when moves are processed. If I call moveItemAtIndexPath
and deleteItemsAtIndexPaths
in the same performBatchUpdates
, should the move indexes be relative to the pre- or post-deleted order? How about insertItemsAtIndexPaths
?
Finally, I'm facing issues calling reloadItemsAtIndexPaths
and moveItemAtIndexPath
in the same operation:
Fatal Exception: NSInternalInconsistencyException attempt to delete and reload the same index path
Is there a way to do all the operations I want in the same performBatchUpdates
? If so, what order do the updates get processed relative to the others? If not, what do people usually do? Reload the data after doing all other operations? Before? I'd much prefer if all the animations happened in a single stage.
Mark's answer is right. I'd recommend watching WWDC's 2018 Session 225 "A Tour of UICollectionView" to get the full explanation from an Apple engineer.
You can skip to the 33'36" mark for the interesting bit.
PerformBatchUpdates
To reload an item that is moved, call all reloads in a separate PerformBatchUpdates
, inside a PerformWithoutAnimation
(as reloads are never animated).
For the move operations, the from indexPath is pre-delete indices, and the to indexPath is post-delete indices. Reloads should only be specified for indexPaths that have not been inserted, deleted, or moved. This is probably why you're seeing the NSInternalInconsistencyException
.
A handy way to verify the operations are set up correctly: the set of reload, insert and move-to indexPaths should not have any duplicates, and the set of reload, delete, and move-from indexPaths should not have any duplicates.
UPDATE:
It appears that items that you move are not also updated, but only moved. So, if you need to update and move an item, you can perform the reload before or after the batch update (depending on the state of your data source).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With