UICollectionView声明失败
我得到这个错误在UICollectionView
执行insertItemsAtIndexPaths
断言在以下失败:
-[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
那么你的代表callback将是:
- (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]; } }
这种方法的信用归功于Blake Watters。
这是一个非黑客,基于文档的答案的问题。 在我的情况下,有一个条件,我会从collectionView:viewForSupplementaryElementOfKind:atIndexPath:
返回一个有效的或无补充的视图。 遇到崩溃后,我检查了文档,这是他们说的:
这个方法必须总是返回一个有效的视图对象。 如果您不想在特定情况下使用补充视图,则布局对象不应该为该视图创build属性。 或者,您可以通过将相应属性的隐藏属性设置为YES来隐藏视图,或者将属性的alpha属性设置为0.要隐藏stream布局中的页眉和页脚视图,还可以设置这些视图的宽度和高度为0。
还有其他方法可以做到这一点,但最快的是:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section { return <condition> ? collectionViewLayout.headerReferenceSize : CGSizeZero; }
我的collections视图是从两个数据源获取项目,并更新它们导致此问题。 我的解决方法是将数据更新和集合视图重新加载到一起:
[[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
我也遇到了这个问题。 这是发生在我身上的事情:
- 我subclassed UICollectionViewController和
initWithCollectionViewLayout:
:,正在初始化我的NSFetchedResultsController
。 - 有一个共享类从NSURLConnection获取结果并parsingJSONstring(不同的线程)
- 通过饲料循环,并创build我的
NSManagedObjects
,将它们添加到我的NSManagedObjectContext
,它具有主线程的NSManagedObjectContext
作为parentContext
。 - 保存我的上下文。
- 让我的
NSFetchedResultsController
拿起更改并排队。 - On
- (void)controllerDidChangeContent:
UICollectionView
我会处理更改并将其应用于我的UICollectionView
。
间歇性地,我会得到OP的错误,不知道为什么。
为了解决这个问题,我把NSFetchedResultsController
初始化和performFetch
移到了my - viewDidLoad
方法中,这个问题现在已经消失了。 不需要调用[collectionView reloadData]
或其他所有的animation都可以正常工作。
希望这可以帮助!
当插入或移动单元格到包含补充页眉或页脚视图的部分(使用UICollectionViewFlowLayout
或从中派生的布局),并且该部分在插入/移动之前的单元数为0时,似乎会出现问题。
我只能避开崩溃,并通过在包含补充标题视图的部分中有一个空的和不可见的单元格来保持animation,如下所示:
- 使
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section
返回实际单元格count + 1为该节的标题视图。 -
在
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
返回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"];
- 使用
moveItemAtIndexPath:
或insertItemsAtIndexPaths:
不会崩溃。
这是我在项目中使用的这个错误的一个解决scheme,我想我会在这里发布,以防发现它有价值。
@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]; }
在我的情况下,问题是我创build我的NSIndexPath
。 例如,要删除第三个单元格,而不是:
NSIndexPath* indexPath = [NSIndexPath indexPathWithIndex:2]; [_collectionView deleteItemsAtIndexPaths:@[indexPath]];
我需要做的是:
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:2 inSection:0]; [_collectionView deleteItemsAtIndexPaths:@[indexPath]];
检查您是否在numberOfSectionsInCollectionView:
返回正确的值numberOfSectionsInCollectionView:
我用来计算部分的值是零,因此0
部分。 这引起了例外。
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { NSInteger sectionCount = self.objectThatShouldHaveAValueButIsActuallyNil.sectionCount; // section count is wrong! return sectionCount; }
只是为了logging,我遇到了同样的问题,对我来说,解决scheme是删除标题(在.xib中禁用它们),因为它们不再需要删除此方法。 之后,一切似乎都很好。
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementary ElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
我自己碰到这个问题 这里的所有答案似乎都带来了问题,除了Alex L s。 提到更新似乎是答案。 这是我的最终解决scheme:
- (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]]]; }]; }
实际工作的解决方法是,如果补充视图的索引path中的单元格不存在(初始加载,删除行等),则返回高度0。 在这里看到我的答案: