Ошибка UICollectionView Assertion

Я получаю эту ошибку при выполнении insertItemsAtIndexPaths в UICollectionView

Ошибка утверждения:

 -[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' 

Я проверил, и мой источник данных содержит только один элемент. Любые идеи о том, почему это может произойти? Если требуется дополнительная информация, я могу определенно предоставить это.

Я столкнулся с этой самой проблемой при вставке первой ячейки в представление коллекции. Я исправил проблему, изменив свой код, чтобы я вызывал UICollectionView

 - (void)reloadData 

метод при вставке первой ячейки, но

 - (void)insertItemsAtIndexPaths:(NSArray *)indexPaths 

при вставке всех других ячеек.

Интересно, что у меня также была проблема с

 - (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths 

при удалении последней ячейки. Я сделал то же самое, что и раньше: просто вызовите reloadData при удалении последней ячейки.

Вставка раздела # 0 непосредственно перед вставкой ячеек делает UICollectionView счастливым.

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

Я написал для этой проблемы работу: https://gist.github.com/iwasrobbed/5528897

В частной категории в верхней части вашего файла .m :

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

Затем ваши обратные вызовы делегатов будут:

 - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { shouldReloadCollectionView = NO; blockOperation = [NSBlockOperation new]; } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id)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]; } } 

Кредит для этого подхода относится к Блейку Уоттеру.

Ниже приведен небезопасный ответ на проблему, основанный на документах. В моем случае было условие, согласно которому я вернул бы действительный или нулевой дополнительный вид из collectionView:viewForSupplementaryElementOfKind:atIndexPath: После столкновения, я проверил документы и вот что они говорят:

Этот метод должен всегда возвращать действительный объект вида. Если вам не нужен дополнительный просмотр в конкретном случае, ваш объект макета не должен создавать атрибуты для этого представления. Кроме того, вы можете скрыть представления, установив для скрытого свойства соответствующих атрибутов значение YES или установить для свойства alpha атрибутов значение 0. Чтобы скрыть представления заголовка и нижнего колонтитула в макете streamа, вы также можете установить ширину и высоту этих представлений до 0.

Есть и другие способы сделать это, но наиболее быстрым кажется:

 - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section { return  ? collectionViewLayout.headerReferenceSize : CGSizeZero; } 

В моем представлении коллекции были элементы из двух источников данных, и их обновление вызвало эту проблему. Мое обходное решение состояло в том, чтобы перезагрузить обновление данных и сборку:

 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ //Update Data Array weakSelf.dataProfile = [results lastObject]; //Reload CollectionView [weakSelf.collectionView reloadItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:0]]]; }]; 

Убедитесь, что вы возвращаете правильное количество элементов в методах UICollectionViewDataSource :

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

а также

 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView 

Я столкнулся с этой проблемой. Вот что случилось со мной:

  1. Я подclassифицировал UICollectionViewController и, в initWithCollectionViewLayout: инициализировал мой NSFetchedResultsController .
  2. Получите общие результаты выборки classа из NSURLConnection и проанализируйте строку JSON (другой stream)
  3. Прокрутите фид и создайте мои NSManagedObjects , добавьте их в свой NSManagedObjectContext , который имеет NSManagedObjectContext основного streamа в качестве parentContext .
  4. Сохраните мой контекст.
  5. Попросите NSFetchedResultsController забрать изменения и NSFetchedResultsController их в очередь.
  6. On - (void)controllerDidChangeContent: я бы обработал изменения и применил их к моему UICollectionView .

С перерывами я получаю ошибку, которую получает OP, и не мог понять, почему.

Чтобы исправить эту проблему, я переместил инициализацию NSFetchedResultsController и performFetch в метод my - viewDidLoad и эта проблема теперь исчезла. Нет необходимости вызывать [collectionView reloadData] или что-то еще, и все анимации работают правильно.

Надеюсь это поможет!

Кажется, что проблема возникает, когда вы вставляете или перемещаете ячейку в раздел, который содержит дополнительный заголовок или нижний колонтитул (с UICollectionViewFlowLayout или макет, полученный из этого), и секция имеет счет 0 ячеек перед вставкой / перемещением.

Я мог только обойти крушение и по-прежнему поддерживать анимацию, имея пустую и невидимую ячейку в секции, содержащей дополнительное представление заголовка следующим образом:

  1. Make - (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section возвращает фактический номер ячейки `count + 1 для этого раздела, где находится заголовок.
  2. В - (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]; } 

    … пустую ячейку для этой позиции. Не забудьте зарегистрировать повторное использование ячеек в viewDidLoad или везде, где вы инициализируете свой UICollectionView:

     [self.collectionView registerClass:[UICollectionReusableView class] forCellWithReuseIdentifier:@"EmptyCell"]; 
  3. Используйте moveItemAtIndexPath: или insertItemsAtIndexPaths: без сбоев.

Вот решение для этой ошибки, которую я использовал в своих проектах, я думал, что опубликую здесь, если кто-нибудь найдет это ценным.

 @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 )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]; } 

В моем случае проблема заключалась в том, как я создавал свой NSIndexPath . Например, чтобы удалить 3-я ячейка, вместо того, чтобы делать:

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

Мне нужно было сделать:

 NSIndexPath* indexPath = [NSIndexPath indexPathForItem:2 inSection:0]; [_collectionView deleteItemsAtIndexPaths:@[indexPath]]; 

Убедитесь, что вы возвращаете правильное значение в numberOfSectionsInCollectionView:

Значение, которое я использовал для вычисления секций, было nil, таким образом, 0 секций. Это вызвало исключение.

 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { NSInteger sectionCount = self.objectThatShouldHaveAValueButIsActuallyNil.sectionCount; // section count is wrong! return sectionCount; } 

Только для записи я столкнулся с той же проблемой, и для меня решение заключалось в том, чтобы удалить заголовок (отключить их в .xib), и поскольку они больше не нужны, этот метод удаляется. После этого все кажется прекрасным.

 - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementary ElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath 

Я сам попал в эту проблему. Все ответы здесь представляли проблемы, за исключением Alex L’s. Обращение к обновлению, казалось, было ответом. Вот мое окончательное решение:

 - (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, если ячейка на пути указателя дополнительного вида отсутствует (начальная загрузка, вы удалили строку и т. Д.). См. Мой ответ здесь:

https://stackoverflow.com/a/18411860/917104

  • iPhone - UILabel, содержащий текст с несколькими шрифтами одновременно
  • Метод Swizzle на устройстве iPhone
  • Получить нажатие кнопки внутри UITableViewCell
  • Получение видимого прямоугольника содержимого UIScrollView
  • Позиционирование MKMapView для одновременного отображения нескольких аннотаций
  • Разработка iPhone: освобожден указатель
  • UIWebView - Как определить последнее сообщение webViewDidFinishLoad?
  • Как установить получателей для UIActivityViewController в iOS 6?
  • Отправить почту без MFMailComposeViewController
  • Лучший подход для синтаксического анализа XML на iPhone
  • Изучение основ UIScrollView
  • Давайте будем гением компьютера.