UICollectionView Assertion failure UICollectionView Assertion failure objective-c objective-c

UICollectionView Assertion failure


I ran into this very same problem when inserting the first cell into a collection view. I fixed the problem by changing my code so that I call the UICollectionView

- (void)reloadData

method when inserting the first cell, but

- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths

when inserting all other cells.

Interestingly, I also had a problem with

- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths

when deleting the last cell. I did the same thing as before: just call reloadData when deleting the last cell.


Inserting section#0 just before inserting cells seems make UICollectionView happy.

NSArray *indexPaths = /* indexPaths of the cells to be inserted */NSUInteger countBeforeInsert = _cells.count;dispatch_block_t updates = ^{    if (countBeforeInsert < 1) {        [self.collectionView insertSections:[NSIndexSet indexSetWithIndex:0]];    }    [self.collectionView insertItemsAtIndexPaths:indexPaths];};[self.collectionView performBatchUpdates:updates completion:nil];


I've posted a work around for this issue here: https://gist.github.com/iwasrobbed/5528897

In the private category at the top of your .m file:

@interface MyViewController (){    BOOL shouldReloadCollectionView;    NSBlockOperation *blockOperation;}@end

Then your delegate callbacks would be:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller{    shouldReloadCollectionView = NO;    blockOperation = [NSBlockOperation new];}- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo           atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type{    __weak UICollectionView *collectionView = self.collectionView;    switch (type) {        case NSFetchedResultsChangeInsert: {            [blockOperation addExecutionBlock:^{                [collectionView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]];            }];            break;        }        case NSFetchedResultsChangeDelete: {            [blockOperation addExecutionBlock:^{                [collectionView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]];            }];            break;        }        case NSFetchedResultsChangeUpdate: {            [blockOperation addExecutionBlock:^{                [collectionView reloadSections:[NSIndexSet indexSetWithIndex:sectionIndex]];            }];            break;        }        default:            break;    }}- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath{    __weak UICollectionView *collectionView = self.collectionView;    switch (type) {        case NSFetchedResultsChangeInsert: {            if ([self.collectionView numberOfSections] > 0) {                if ([self.collectionView numberOfItemsInSection:indexPath.section] == 0) {                    shouldReloadCollectionView = YES;                } else {                    [blockOperation addExecutionBlock:^{                        [collectionView insertItemsAtIndexPaths:@[newIndexPath]];                    }];                }            } else {                shouldReloadCollectionView = YES;            }            break;        }        case NSFetchedResultsChangeDelete: {            if ([self.collectionView numberOfItemsInSection:indexPath.section] == 1) {                shouldReloadCollectionView = YES;            } else {                [blockOperation addExecutionBlock:^{                    [collectionView deleteItemsAtIndexPaths:@[indexPath]];                }];            }            break;        }        case NSFetchedResultsChangeUpdate: {            [blockOperation addExecutionBlock:^{                [collectionView reloadItemsAtIndexPaths:@[indexPath]];            }];            break;        }        case NSFetchedResultsChangeMove: {            [blockOperation addExecutionBlock:^{                [collectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath];            }];            break;        }        default:            break;    }}- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller{    // Checks if we should reload the collection view to fix a bug @ http://openradar.appspot.com/12954582    if (shouldReloadCollectionView) {        [self.collectionView reloadData];    } else {        [self.collectionView performBatchUpdates:^{            [blockOperation start];        } completion:nil];    }}

Credit for this approach goes to Blake Watters.