UICollectionViewData中的断言失败:indexPathForItemAtGlobalIndex

28

我正在使用performBatchUpdates()来更新我的集合视图,在其中进行完全刷新,即删除其中的所有内容并重新插入所有内容。批量更新是作为一个观察者的一部分完成的,该观察者附加到NSMutableArray (bingDataItems)上。

cellItems是包含将要插入到集合视图中的项目的数组。

以下是代码:

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    cultARunner *_cultARunner = [cultARunner getInstance];
    if ( [[_cultARunner bingDataItems] count] ) {
        [self.collectionView reloadData];
        [[self collectionView] performBatchUpdates: ^{

            int itemSize = [cellItems count];
            NSMutableArray *arrayWithIndexPaths = [NSMutableArray array];

            // first delete the old stuff
            if (itemSize == 0) {
                [arrayWithIndexPaths addObject: [NSIndexPath indexPathForRow: 0 inSection: 0]];
            }
            else {
                for( int i = 0; i < cellItems.count; i++ ) {
                    [arrayWithIndexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
                }
            }
            [cellItems removeAllObjects];
            if(itemSize) {
                [self.collectionView deleteItemsAtIndexPaths:arrayWithIndexPaths];
            }

            // insert the new stuff
            arrayWithIndexPaths = [NSMutableArray array];
            cellItems = [_cultARunner bingDataItems];
            if ([cellItems count] == 0) {
                [arrayWithIndexPaths addObject: [NSIndexPath indexPathForRow: 0 inSection: 0]];
            }
            else {              
                for( int i = 0; i < [cellItems count]; i++ ) {
                    [arrayWithIndexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
                }
            }
            [self.collectionView insertItemsAtIndexPaths:arrayWithIndexPaths];
        }
        completion:nil];
    }
}

我遇到了这个错误,但并不总是出现(为什么?)

2012-12-16 13:17:59.789 [16807:19703] *** Assertion failure in -[UICollectionViewData indexPathForItemAtGlobalIndex:], /SourceCache/UIKit_Sim/UIKit-2372/UICollectionViewData.m:442
2012-12-16 13:17:59.790 [16807:19703] DEBUG:    request for index path for global index 1342177227 when there are only 53 items in the collection view

我检查了唯一一个提到相同问题的线程:UICollectionView Assertion failure,但它不是很清楚,即在performBatchUpdates()块中执行[collectionview reloadData]是不可取的。

你有什么建议吗?


这里是另一个问题的原因和解决方案:https://fangpenlin.com/posts/2016/04/29/uicollectionview-invalid-number-of-items-crash-issue/ - abc123
8个回答

20

终于解决了!好的,这就是导致我的应用崩溃的原因。

正如之前所述,我创建了补充视图,以提供自定义样式的分区标题给我的集合视图。

问题在于:似乎补充视图的indexPath必须对应于集合视图中已存在的cell的indexPath。如果补充视图的indexPath没有对应的普通cell,应用程序将崩溃。我相信,在更新过程中,集合视图试图检索补充视图单元格的信息,所以当它找不到时就会崩溃。

希望这也能解决你的问题!


2
很好,这就是正确的答案!没有项目的部分容易崩溃。 - Felipe Baytelman
Ash,谢谢你,我的应用程序也有这种崩溃问题,你真的帮了我。 - Alexander Tkachenko
很好的答案。这是令人讨厌的,因为你不能在同一个performBatchUpdates中将一个项目从要删除的部分“移动”出来..所以如果你将所有项目都移出一个部分,我发现唯一有效的方法是在移动项目时向该部分添加一个临时虚拟不可见单元格,然后在performBatchUpdates完成时删除该部分..尽管我可能漏掉了某些东西。 - chasew
抱歉,我不明白什么是解决方案。在我的cellforindex中,我不应该使用.item吗? - PoolHallJunkie
请注意,这个问题已经四年了,在最近的iOS更新中,集合视图进行了大规模的改进。我相当确定在写作时这是正确的,因为我记得自己进行了广泛的测试,但不排除自那时以来苹果公司对其进行了更改。 - Ash
显示剩余2条评论

17
这是解决崩溃的适当方法:
你的每个补充视图都与某个索引路径相关联。 如果在该索引路径上没有单元格(初始加载、已删除行等),则通过布局的代理返回补充视图的高度为0。
因此,对于流式布局,实现UICollectionViewDelegateFlowLayout的方法。
(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section

使用以下逻辑的方法(以及相应的页脚方法,如果您正在使用页脚)

if ( you-have-a-cell-at-the-row-for-this-section )
    return myNormalHeaderSize;
else return CGSizeMake( 0,0 );

希望这可以帮助到你!


我喜欢这样的情况:一个问题已经存在了9个月,而我在正确解决方案被找到4天后才发现它! - cyphers
2
如果该部分为空,但我仍希望显示该部分标题怎么办? - onlygo
@SunJian 解决方法是与您的标题相关联的单元格必须存在,因此您必须始终拥有一个单元格,以便使您的部分标题可见。 - deepseadiving
@SunJian 值得注意的是,你不应该把这个视图看作是一个章节标题。它实际上是一个补充视图;它是为了特定的单元格而存在的。所以如果这个单元格不存在,你的补充视图要么为空,要么高度为0。 - deepseadiving

8
我的reloadData无法使用,因为使用performBatchUpdates的目的是要获得动画效果。如果你使用reloadData,你只会刷新数据,但没有动画效果。
因此,“用reloadData替换performBatchUpdates”的建议基本上是在说“放弃你想做的事情”。
很抱歉,我很沮丧,因为我在尝试进行一些很棒的动画更新时,这个错误一直出现,而我的模型是100%正确的,是一些iOS魔术被破坏了,迫使我完全改变我的解决方案。
我的观点是,集合视图仍然存在故障,并且不能进行复杂的动画刷新,尽管它们应该能够做到这一点。因为表格视图也曾经有过这样的问题,但现在已经相当稳定了(虽然花费了一些时间)。
//编辑(2013年9月1日) 报告的 bug 现在已经关闭了,所以这个问题似乎已经被 Apple 解决了。

1
我建议制作一个简单的示例,并将其作为错误提交给Apple。 - bshirley
2
我4周前做了那件事,他们将其标记为重复,并且原始错误仍然未被解决。所以情况不太妙。 - czechboy
1
我发现了一种解决问题的方法,可以让你在等待永久解决方案的同时继续开发。如果你还没有创建自定义布局对象,请创建一个,重写prepareForCollectionViewUpdates:方法,并且不要调用[super prepareForCollectionViewUpdates:]。这可能会对其他地方产生连锁反应,而且并不是永久性的解决方案,但至少可以让该死的东西工作起来。 - Ash
3
谢谢,Ash。我基本上使用了你的解决方案(覆盖prepareForCollectionViewUpdates:而不是从不调用super)。我只是用try/catch包装它。在我进行的少量测试中,这似乎是有效的。将其包装在@try{}@catch{}中还有一个额外的好处,就是当错误被修复后,我无需更改任何内容。 - Derrick Hathaway
1
即使在iOS 7中,这似乎仍然是一个问题。感谢Ash&Derrick的想法!顺便说一句,如果您使用try-catch方法,请记得对该文件使用“-fobjc-arc-exception”。 - Jonathan Sterling
显示剩余3条评论

3

我遇到了同样的问题。

我尝试了多种方法后,最终似乎起作用的是[self.collectionView reloadData],其中"self.collectionView"是您的集合视图的名称。

我尝试使用直接从“UICollectionView Class Reference”中提取的以下方法:插入、移动和删除项目。

首先使用它们将项目从一部分“移动”到另一部分。

  • deleteItemsAtIndexPaths:

  • insertItemsAtIndexPaths:

接下来,我尝试使用moveItemAtIndexPath:toIndexPath:

它们都会产生以下错误:

-[UICollectionViewData indexPathForItemAtGlobalIndex:]中的断言失败:/SourceCache/UIKit_Sim/UIKit-2372/UICollectionViewData.m:442

所以,请尝试使用"reloadData"方法。


1
你的代码中确切地在哪里调用了'reloadData'?是在'viewDidLoad'和/或其他地方吗? - DancingJohn

2
如果您从包含页眉/页脚的部分中删除最后一个单元格,则会出现错误。
我尝试在那时返回头/脚大小/元素的 nil,有时可以解决问题。
选项:
1.重新加载整个表视图,而不是动画删除最后一项。
2.添加一个额外的不可见的基本单元格,其大小小于1。

2
一个常见的错误是在同一个视图控制器上多次重复使用相同的UICollectionViewFlowLayout,这可能会导致出现此错误!只需为每个collectionview初始化不同的flowLayouts,问题就可以得到解决!

1
谢天谢地,我找到了你的提示,不要为多个集合视图共享相同的布局。我差点把头发都拔光了。 - Stefan Arn

1
我在删除集合视图中的一个单元格时遇到了这个问题。
问题是我使用了自定义布局,并且调用layoutAttributesForElementsInRect返回的单元格数比删除后的集合视图中的单元格数多。
显然,UICollectionView只是迭代该方法返回的数组,而没有检查单元格的数量。
修改该方法以返回相同数量的布局属性解决了崩溃。

0

我仍然无法弄清楚全局索引是如何增加的,但我通过在基础数据源数组中插入临时项(即cellItems),并在viewDidLoad()中调用[self.collectionview reloadData]来解决了我的问题。

这将在集合视图中暂时插入一个占位符单元格,直到我使用performBatchUpdates()触发实际过程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接