Error UICollectionView Assertion

I get this error while executing insertItemsAtIndexPaths in a UICollectionView

Validation error in:

 -[UICollectionViewData indexPathForItemAtGlobalIndex:], /SourceCache/UIKit/UIKit-2372/UICollectionViewData.m:442 2012-09-26 18:12:34.432 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'request for index path for global index 805306367 when there are only 1 items in the collection view' 

I checked and my data source contains only one element. Any ideas on why this might happen? If you need more information, I can definitely provide this.

+58
objective-c ios6 uicollectionview
Sep 26 '12 at 22:16
source share
14 answers

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

 - (void)reloadData 

when you insert the first cell, but

 - (void)insertItemsAtIndexPaths:(NSArray *)indexPaths 

when pasting all other cells.

Interestingly, I also had a problem with

 - (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths 

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

+67
Oct 19 '12 at 18:08
source share

Inserting section # 0 just before inserting cells seems to 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]; 
+11
Mar 18 '13 at 23:17
source share

I published a work for this problem 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 will be as follows:

 - (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]; } } 

The credit for this approach relates to Blake Water.

+5
May 6 '13 at 10:55
source share

The following is an unsafe answer to a document-based problem. In my case, there was a condition according to which I would return a valid or null optional view from collectionView:viewForSupplementaryElementOfKind:atIndexPath: After the collision, I checked the documents and this is what they say:

This method should always return a valid view object. If you do not want an additional view in a particular case, your layout object should not create attributes for this view. In addition, you can hide the views by setting the hidden property of the corresponding attributes to YES, or set the alpha property to 0. To hide the header and footer in the stream layout, you can also set the width and height of these views to 0.

There are other ways to do this, but the fastest:

 - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section { return <condition> ? collectionViewLayout.headerReferenceSize : CGSizeZero; } 
+4
Sep 20 '13 at 9:53 on
source share

In my view of the collection, there were elements from two data sources, and updating them caused this problem. My workaround was to restart the data update and data collection:

 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ //Update Data Array weakSelf.dataProfile = [results lastObject]; //Reload CollectionView [weakSelf.collectionView reloadItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:0]]]; }]; 
+3
May 01 '13 at 23:39
source share

Make sure you return the correct number of elements in the UICollectionViewDataSource methods:

 - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 

and

 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView 
+1
Sep 27
source share

I ran into this problem. Here's what happened to me:

  • I subclassed the UICollectionViewController and on initWithCollectionViewLayout: initialized my NSFetchedResultsController .
  • Get the general results of fetching a class from NSURLConnection and parse the JSON string (another thread)
  • Scroll the feed and create my NSManagedObjects , add them to my NSManagedObjectContext , which has the main NSManagedObjectContext stream as parentContext .
  • Save my context.
  • NSFetchedResultsController my NSFetchedResultsController to the changes and leave them in the queue.
  • In - (void)controllerDidChangeContent: I processed the changes and applied them to my UICollectionView .

With interruptions, I get the error that the OP gets, and could not understand why.

To fix this problem, I transferred the initialization of NSFetchedResultsController and performFetch to my method - viewDidLoad , and this problem has now disappeared. You do not need to call [collectionView reloadData] or anything, and all animations work correctly.

Hope this helps!

+1
Mar 25 '13 at 17:27
source share

It seems that the problem occurs when you insert or move a cell into a section that contains an extra header or footer (with a UICollectionViewFlowLayout or layout obtained from this), and there are a number of 0 cells in the section before insert / move.

I could only get around the crash and still support the animation, having an empty and invisible cell in the section containing an additional header representation as follows:

  • Do - (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section to return the actual cell number `count + 1 for this section where the header is located.
  • B - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath return

     if ((indexPath.section == YOUR_SECTION_WITH_THE_HEADER_VIEW) && (indexPath.item == [self collectionView:collectionView numberOfItemsInSection:indexPath.section] - 1)) { cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"EmptyCell" forIndexPath:indexPath]; } 

    ... empty cell for this position. Remember to register cell reuse in viewDidLoad or wherever you initialize your UICollectionView:

     [self.collectionView registerClass:[UICollectionReusableView class] forCellWithReuseIdentifier:@"EmptyCell"]; 
  • Use moveItemAtIndexPath: or insertItemsAtIndexPaths: without fail.
+1
Apr 22 '13 at 16:01
source share

Here is the solution for this error that I used in my projects, I thought I would post it here in case anyone finds it valuable.

 @interface FetchedResultsViewController () @property (nonatomic) NSMutableIndexSet *sectionsToAdd; @property (nonatomic) NSMutableIndexSet *sectionsToDelete; @property (nonatomic) NSMutableArray *indexPathsToAdd; @property (nonatomic) NSMutableArray *indexPathsToDelete; @property (nonatomic) NSMutableArray *indexPathsToUpdate; @end 
 #pragma mark - NSFetchedResultsControllerDelegate - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { [self resetFetchedResultControllerChanges]; } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { switch(type) { case NSFetchedResultsChangeInsert: [self.sectionsToAdd addIndex:sectionIndex]; break; case NSFetchedResultsChangeDelete: [self.sectionsToDelete addIndex:sectionIndex]; break; } } - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { switch(type) { case NSFetchedResultsChangeInsert: [self.indexPathsToAdd addObject:newIndexPath]; break; case NSFetchedResultsChangeDelete: [self.indexPathsToDelete addObject:indexPath]; break; case NSFetchedResultsChangeUpdate: [self.indexPathsToUpdate addObject:indexPath]; break; case NSFetchedResultsChangeMove: [self.indexPathsToAdd addObject:newIndexPath]; [self.indexPathsToDelete addObject:indexPath]; break; } } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { if (self.sectionsToAdd.count > 0 || self.sectionsToDelete.count > 0 || self.indexPathsToAdd.count > 0 || self.indexPathsToDelete > 0 || self.indexPathsToUpdate > 0) { if ([self shouldReloadCollectionViewFromChangedContent]) { [self.collectionView reloadData]; [self resetFetchedResultControllerChanges]; } else { [self.collectionView performBatchUpdates:^{ if (self.sectionsToAdd.count > 0) { [self.collectionView insertSections:self.sectionsToAdd]; } if (self.sectionsToDelete.count > 0) { [self.collectionView deleteSections:self.sectionsToDelete]; } if (self.indexPathsToAdd.count > 0) { [self.collectionView insertItemsAtIndexPaths:self.indexPathsToAdd]; } if (self.indexPathsToDelete.count > 0) { [self.collectionView deleteItemsAtIndexPaths:self.indexPathsToDelete]; } for (NSIndexPath *indexPath in self.indexPathsToUpdate) { [self configureCell:[self.collectionView cellForItemAtIndexPath:indexPath] atIndexPath:indexPath]; } } completion:^(BOOL finished) { [self resetFetchedResultControllerChanges]; }]; } } } // This is to prevent a bug in UICollectionView from occurring. // The bug presents itself when inserting the first object or deleting the last object in a collection view. // http://stackoverflow.com/questions/12611292/uicollectionview-assertion-failure // This code should be removed once the bug has been fixed, it is tracked in OpenRadar // http://openradar.appspot.com/12954582 - (BOOL)shouldReloadCollectionViewFromChangedContent { NSInteger totalNumberOfIndexPaths = 0; for (NSInteger i = 0; i < self.collectionView.numberOfSections; i++) { totalNumberOfIndexPaths += [self.collectionView numberOfItemsInSection:i]; } NSInteger numberOfItemsAfterUpdates = totalNumberOfIndexPaths; numberOfItemsAfterUpdates += self.indexPathsToAdd.count; numberOfItemsAfterUpdates -= self.indexPathsToDelete.count; BOOL shouldReload = NO; if (numberOfItemsAfterUpdates == 0 && totalNumberOfIndexPaths == 1) { shouldReload = YES; } if (numberOfItemsAfterUpdates == 1 && totalNumberOfIndexPaths == 0) { shouldReload = YES; } return shouldReload; } - (void)resetFetchedResultControllerChanges { [self.sectionsToAdd removeAllIndexes]; [self.sectionsToDelete removeAllIndexes]; [self.indexPathsToAdd removeAllObjects]; [self.indexPathsToDelete removeAllObjects]; [self.indexPathsToUpdate removeAllObjects]; } 
+1
Mar 07 '14 at 16:27
source share

In my case, the problem was how I created my NSIndexPath . For example, to remove the third cell, instead of doing:

 NSIndexPath* indexPath = [NSIndexPath indexPathWithIndex:2]; [_collectionView deleteItemsAtIndexPaths:@[indexPath]]; 

I needed to do:

 NSIndexPath* indexPath = [NSIndexPath indexPathForItem:2 inSection:0]; [_collectionView deleteItemsAtIndexPaths:@[indexPath]]; 
+1
Mar 18 '16 at 16:38
source share

Make sure you return the correct value in numberOfSectionsInCollectionView:

The value I used to calculate the sections was nil, thus 0 . This caused an exception.

 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { NSInteger sectionCount = self.objectThatShouldHaveAValueButIsActuallyNil.sectionCount; // section count is wrong! return sectionCount; } 
+1
May 24 '16 at 9:00
source share

Just for the record, I ran into the same problem, and for me the solution was to remove the header (disable them in .xib), and since they are no longer needed, this method is deleted. After that, everything seems beautiful.

 - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementary ElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath 
0
Apr 26 '13 at 13:36 on
source share

I myself got into this problem. All answers here presented problems, with the exception of Alex L's. Addressing the update seemed to be the answer. Here is my final decision:

 - (void)addItem:(id)item { [[NSOperationQueue mainQueue] addOperationWithBlock:^{ if (!_data) { _data = [NSMutableArray arrayWithObject:item]; } else { [_data addObject:item]; } [_collectionView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:_data.count-1 inSection:0]]]; }]; } 
0
Jun 18 '13 at 8:05
source share

The workaround that actually works is to return a height of 0 if there is no cell in the secondary view index path (boot, you deleted the line, etc.). See my answer here:

stack overflow

0
Aug 23 '13 at 21:11
source share



All Articles