Brent Traut Brent Traut - 3 months ago 84
Swift Question

How to order moves, inserts, deletes, and updates in a UICollectionView performBatchUpdates block?

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.

Answer

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).