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'
チェックしましたが、データソースに含まれる要素は1つだけです。これが起こる理由についての洞察はありますか?さらに情報が必要な場合は、間違いなく提供できます。
最初のセルをコレクションビューに挿入するときに、これとまったく同じ問題に遭遇しました。 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<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:
から有効またはゼロの補足ビューを返す条件がありました。クラッシュに遭遇した後、私はドキュメントをチェックしましたが、ここに彼らの言うことがあります:
このメソッドは、常に有効なビューオブジェクトを返す必要があります。特定のケースで補足ビューが必要ない場合は、レイアウトオブジェクトでそのビューの属性を作成しないでください。または、対応する属性のhiddenプロパティをYESに設定するか、属性のalphaプロパティを0に設定してビューを非表示にできます。フローレイアウトでヘッダービューとフッタービューを非表示にするには、これらのビューの幅と高さも設定できます0に。
これを行う方法は他にもありますが、最も簡単な方法は次のとおりです。
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
return <condition> ? collectionViewLayout.headerReferenceSize : CGSizeZero;
}
私のコレクションビューは2つのデータソースからアイテムを取得し、それらを更新するとこの問題が発生しました。私の回避策は、データ更新とコレクションビューのリロードを一緒にキューに入れることでした。
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//Update Data Array
weakSelf.dataProfile = [results lastObject];
//Reload CollectionView
[weakSelf.collectionView reloadItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:0]]];
}];
プロジェクトで使用してきたこのバグの解決策を以下に示します。貴重なものが見つかった場合に備えて、ここに投稿すると思いました。
@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];
}
私の場合、問題は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;
}
私もこの問題に遭遇しました。私に起こったことは次のとおりです。
initWithCollectionViewLayout:
_でNSFetchedResultsController
を初期化していました。NSManagedObjects
を作成し、メインスレッドのNSManagedObjectContext
をNSManagedObjectContext
として持つparentContext
に追加します。NSFetchedResultsController
に変更を選択させ、それらをキューに入れます。- (void)controllerDidChangeContent:
で、変更を処理してUICollectionView
に適用します。断続的に、OPが取得しているエラーが表示され、理由がわかりませんでした。
この問題を修正するために、NSFetchedResultsController
初期化とperformFetch
を_- viewDidLoad
_メソッドに移動しましたが、この問題はなくなりました。 _[collectionView reloadData]
_または何かを呼び出す必要はなく、すべてのアニメーションは正常に動作しています。
お役に立てれば!
問題は、補助ヘッダーまたはフッタービューを含むセクション(UICollectionViewFlowLayout
またはそれから派生したレイアウト)にセルを挿入または移動し、セクションのカウントが0セルになると発生するようです挿入/移動。
次のような補足ヘッダービューを含むセクションに空の非表示のセルを配置することで、クラッシュを回避し、アニメーションを維持することができました。
- (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:
クラッシュすることなく。UICollectionViewDataSource
メソッドで正しい要素数を返していることを確認してください。
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
そして
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
記録のためだけに、私は同じ問題にぶつかりました。私にとって解決策はヘッダーを削除することで(.xibでヘッダーを無効にします)、ヘッダーが不要になったため、このメソッドは削除されました。その後、すべてがうまくいくようです。
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementary
ElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
私は自分でこの問題にぶつかりました。ここでの答えはすべて、アレックスLを除いて問題を提示しているように見えました。更新を参照することは答えのように見えました。これが私の最終的な解決策です。
- (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を返すことです。ここで私の答えを参照してください: