61

我在执行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'

我已经检查过了,我的数据源只包含一个元素。关于为什么会发生这种情况的任何见解?如果需要更多信息,我绝对可以提供。

4

14 回答 14

69

将第一个单元格插入集合视图时,我遇到了同样的问题。我通过更改代码来解决问题,以便调用 UICollectionView

- (void)reloadData

插入第一个单元格时的方法,但是

- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths

插入所有其他单元格时。

有趣的是,我也遇到了问题

- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths

删除最后一个单元格时。我做了和以前一样的事情:reloadData删除最后一个单元格时调用。

于 2012-10-19T18:08:56.527 回答
11

在插入单元格之前插入 section#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];
于 2013-03-18T23:17:13.903 回答
5

我在这里发布了解决此问题的方法: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。

于 2013-05-06T22:55:19.680 回答
4

这是对该问题的非黑客,基于文档的答案。就我而言,有一个条件,根据该条件,我将从collectionView:viewForSupplementaryElementOfKind:atIndexPath:. 遇到崩溃后,我检查了文档,这是他们所说的:

此方法必须始终返回有效的视图对象。如果您在特定情况下不想要补充视图,则您的布局对象不应为该视图创建属性。或者,您可以通过将相应属性的 hidden 属性设置为 YES 或将属性的 alpha 属性设置为 0 来隐藏视图。要在流布局中隐藏页眉和页脚视图,您还可以设置这些视图的宽度和高度为 0。

还有其他方法可以做到这一点,但最快的方法似乎是:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
    return <condition> ? collectionViewLayout.headerReferenceSize : CGSizeZero;
}
于 2013-09-20T09:53:36.207 回答
3

我的集合视图从两个数据源获取项目并更新它们导致了这个问题。我的解决方法是将数据更新和集合视图重新加载一起排队:

[[NSOperationQueue mainQueue] addOperationWithBlock:^{

                //Update Data Array
                weakSelf.dataProfile = [results lastObject]; 

                //Reload CollectionView
                [weakSelf.collectionView reloadItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:0]]];
 }];
于 2013-05-01T23:39:20.867 回答
1

检查您是否在UICollectionViewDataSource方法中返回正确数量的元素:

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

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
于 2012-09-27T03:58:48.177 回答
1

我也遇到了这个问题。这是发生在我身上的事情:

  1. 我将 UICollectionViewController 子类化,并且initWithCollectionViewLayout:正在初始化我的NSFetchedResultsController.
  2. 让共享类从 NSURLConnection 获取结果并解析 JSON 字符串(不同的线程)
  3. 循环浏览提要并创建 my NSManagedObjects,将它们添加到 my NSManagedObjectContext,它的主线程NSManagedObjectContextparentContext.
  4. 保存我的上下文。
  5. 让我NSFetchedResultsController拿起更改并将它们排队。
  6. 在 上- (void)controllerDidChangeContent:,我将处理更改并将它们应用到我的UICollectionView.

间歇性地,我会收到 OP 遇到的错误,但不知道为什么。

为了解决这个问题,我将NSFetchedResultsController初始化移到了performFetch我的- viewDidLoad方法中,这个问题现在已经消失了。无需打电话[collectionView reloadData]或任何事情,所有动画都可以正常工作。

希望这可以帮助!

于 2013-03-25T17:27:32.343 回答
1

当您将单元格插入或移动到包含补充页眉或页脚视图(带有UICollectionViewFlowLayout或从中派生的布局)的部分并且该部分在插入/移动之前的单元格计数为 0 时,似乎会出现问题。

我只能通过在包含补充标题视图的部分中有一个空且不可见的单元格来避免崩溃并仍然保持动画,如下所示:

  1. - (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section为标题视图所在的部分返回实际的单元格`count + 1 。
  2. 作为- (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"];
    
  3. 使用moveItemAtIndexPath:insertItemsAtIndexPaths:不崩溃。
于 2013-04-22T16:01:43.837 回答
1

这是我在项目中一直使用的这个错误的解决方案,我想我会在这里发布,以防有​​人发现它有价值。

@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];
}
于 2014-03-07T16:27:18.533 回答
1

就我而言,问题在于我创建NSIndexPath. 例如,要删除第三个单元格,而不是这样做:

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

我需要做:

NSIndexPath* indexPath = [NSIndexPath indexPathForItem:2 inSection:0];
[_collectionView deleteItemsAtIndexPaths:@[indexPath]];
于 2016-03-18T16:38:39.260 回答
1

检查您是否返回了正确的值numberOfSectionsInCollectionView:

我用来计算截面的值是零,因此是0截面。这导致了异常。

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    NSInteger sectionCount = self.objectThatShouldHaveAValueButIsActuallyNil.sectionCount;

    // section count is wrong!

    return sectionCount;
}
于 2016-05-24T21:00:39.640 回答
0

只是为了记录,我遇到了同样的问题,对我来说,解决方案是删除标题(在 .xib 中禁用它们),因为不再需要它们,所以删除了这个方法。之后一切似乎都很好。

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementary
ElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
于 2013-04-26T13:36:56.550 回答
0

我自己遇到了这个问题。这里的所有答案似乎都存在问题,除了 Alex 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]]];
    }];
}
于 2013-06-18T08:05:05.363 回答
0

实际可行的解决方法是,如果补充视图的索引路径上的单元格不存在(初始加载、您已删除该行等),则返回高度 0。在这里查看我的答案:

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

于 2013-08-23T21:11:10.607 回答