UICollectionView Performing Updates using performBatchUpdates UICollectionView Performing Updates using performBatchUpdates ios ios

UICollectionView Performing Updates using performBatchUpdates


See Inserting, Deleting, and Moving Sections and Items from the "Collection View Programming Guide for iOS":

To insert, delete, or move a single section or item, you must follow these steps:

  1. Update the data in your data source object.
  2. Call the appropriate method of the collection view to insert or delete the section or item.

It is critical that you update your data source before notifying the collection view of any changes. The collection view methods assume that your data source contains the currently correct data. If it does not, the collection view might receive the wrong set of items from your data source or ask for items that are not there and crash your app.

So in your case, you must add an image to the collection view data source first and then call insertItemsAtIndexPaths. The collection view will then ask the data source delegate function to provide the view for the inserted item.


I just implemented that with Swift. So I would like to share my implementation.First initialise an array of NSBlockOperations:

    var blockOperations: [NSBlockOperation] = []

In controller will change, re-init the array:

func controllerWillChangeContent(controller: NSFetchedResultsController) {    blockOperations.removeAll(keepCapacity: false)}

In the did change object method:

    func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {    if type == NSFetchedResultsChangeType.Insert {        println("Insert Object: \(newIndexPath)")        blockOperations.append(            NSBlockOperation(block: { [weak self] in                if let this = self {                    this.collectionView!.insertItemsAtIndexPaths([newIndexPath!])                }            })        )    }    else if type == NSFetchedResultsChangeType.Update {        println("Update Object: \(indexPath)")        blockOperations.append(            NSBlockOperation(block: { [weak self] in                if let this = self {                    this.collectionView!.reloadItemsAtIndexPaths([indexPath!])                }            })        )    }    else if type == NSFetchedResultsChangeType.Move {        println("Move Object: \(indexPath)")        blockOperations.append(            NSBlockOperation(block: { [weak self] in                if let this = self {                    this.collectionView!.moveItemAtIndexPath(indexPath!, toIndexPath: newIndexPath!)                }            })        )    }    else if type == NSFetchedResultsChangeType.Delete {        println("Delete Object: \(indexPath)")        blockOperations.append(            NSBlockOperation(block: { [weak self] in                if let this = self {                    this.collectionView!.deleteItemsAtIndexPaths([indexPath!])                }            })        )    }}

In the did change section method:

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {    if type == NSFetchedResultsChangeType.Insert {        println("Insert Section: \(sectionIndex)")        blockOperations.append(            NSBlockOperation(block: { [weak self] in                if let this = self {                    this.collectionView!.insertSections(NSIndexSet(index: sectionIndex))                }            })        )    }    else if type == NSFetchedResultsChangeType.Update {        println("Update Section: \(sectionIndex)")        blockOperations.append(            NSBlockOperation(block: { [weak self] in                if let this = self {                    this.collectionView!.reloadSections(NSIndexSet(index: sectionIndex))                }            })        )    }    else if type == NSFetchedResultsChangeType.Delete {        println("Delete Section: \(sectionIndex)")        blockOperations.append(            NSBlockOperation(block: { [weak self] in                if let this = self {                    this.collectionView!.deleteSections(NSIndexSet(index: sectionIndex))                }            })        )    }}

And finally, in the did controller did change content method:

func controllerDidChangeContent(controller: NSFetchedResultsController) {            collectionView!.performBatchUpdates({ () -> Void in        for operation: NSBlockOperation in self.blockOperations {            operation.start()        }    }, completion: { (finished) -> Void in        self.blockOperations.removeAll(keepCapacity: false)    })}

I personally added some code in the deinit method as well, in order to cancel the operations when the ViewController is about to get deallocated:

deinit {    // Cancel all block operations when VC deallocates    for operation: NSBlockOperation in blockOperations {        operation.cancel()    }    blockOperations.removeAll(keepCapacity: false)}


I was facing the similar issue while deleting the item from index and this is what i think we need to do while using performBatchUpdates: method.

1# first call deleteItemAtIndexPath to delete the item from collection view.

2# Delete the element from array.

3# Update collection view by reloading data.

[self.collectionView performBatchUpdates:^{            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:sender.tag inSection:0];            [self.collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];            [self.addNewDocumentArray removeObjectAtIndex:sender.tag];        } completion:^(BOOL finished) {            [self.collectionView reloadData];        }];

This help me to remove all the crash and assertion failures.