导致iOS崩溃的原因是什么?UICollectionView接收具有不存在的索引路径的单元格的布局属性

Dre*_*rew 34 objective-c ios uicollectionview

我正在开发一个具有UICollectionViewController的应用程序,该应用程序在某些难以重现的神秘情况下崩溃.崩溃的日志如下所示:

*** Assertion failure in -[UICollectionViewData validateLayoutInRect:], /SourceCache/UIKit_Sim/UIKit-3318.16.14/UICollectionViewData.m:417
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView received layout attributes for a cell with an index path that does not exist: <NSIndexPath: 0xc000000000008016> {length = 2, path = 0 - 1}'
Run Code Online (Sandbox Code Playgroud)

一旦我们切换到iOS 8 SDK,这样的崩溃似乎才开始在我们的代码中出现.

为什么会这样?

注意:我已经知道问题的答案是什么,但是我发现很少有关于Stack Overflow和网络其他部分崩溃的信息.我将在下面发布答案.这个错误让我的同事和我三天追踪,所以希望这篇文章能为其他人节省很多时间和挫折.我已向Apple提交此错误.

Dre*_*rew 29

崩溃发生在以下情况:

我们有一个集合视图控制器,它在它上面呈现另一个视图控制器.

虽然集合视图控制器不再可见,但偶尔会发生以下事件序列以响应我们的应用程序的后端请求.

  1. [UICollectionView insertItemsAtIndexPaths:]在隐藏的集合视图中调用了50个项目UICollectionViewController.
  2. [UICollectionView reloadData] 被隐藏的集合视图调用.
  3. 会发生短暂的延迟.
  4. 隐藏集合视图中的项目数设置为较小的数字.
  5. [UICollectionView reloadData] 又被召唤了.
  6. 视图控制器被解雇,显示隐藏的集合视图控制器.

内部UIKit类中的断言失败UICollectionViewData将在步骤6发生.

所以,经验教训是,尽量避免操纵在屏幕上看不到的集合视图.

我们解决这个问题的方法是打电话[UICollectionView reloadSections:]而不是[UICollectionView reloadData]关键点.

我们怀疑reloadData未来的某些点会延迟效果,因此可能会出现与其他方法调用如何交互的微妙问题insertItemsAtIndexPaths,而是reloadSections立即处理,使集合视图处于更好的状态.

在我们开始为iOS 8构建应用程序之前,我们认为我们没有看到这种行为.

好好睡吧,我的朋友们!

  • 对我来说和Sean Danzeiser一样.我正在修改单元格计数器的`willSet`观察者中的约束,然后在`UIView.animateWithDuration`方法中调用`layoutIfNeeded`.它在第一轮添加/删除单元格时工作,但是当第二个collectionView变为空,然后尝试再次使用数据填充它时,`layoutIfNeeded`崩溃了.**解决方案是在layoutIfNeeded调用**之前使布局无效. (3认同)

Beu*_*euj 23

对我来说,它是UICollectionViewLayoutAttribute的数组.我正在使用它UICollectionViewLayout来存储项目的属性.

我忘了在prepareLayout方法中清空它.

因此,layoutAttributesForItemAtIndexPath为indexPath返回了不正确的值,导致同样的崩溃.

removeAllprepareLayout开头的数组上只有一个,它正在工作.


Ank*_*ain 8

collectionViewLayout缓存属性.在viewwillappear中 - 创建collectionViewLayout的新实例并将其分配给collectionview.collectionViewLayout以这种方式,所有缓存的属性将在重新加载之前清除您的问题可能已得到解决.为我工作,特别是当您使用其他collectionViewLayout库时.


Eri*_*ere 6

我一直在研究这个相同的bug,并且认为我在iOS 7上找到了另一个错误来源,但在iOS 8上工作正常,导致同样的错误:自动布局!

我正在使用一个嵌入了UICollectionViews的控件,一个网格.我注意到,当与UICollectionView数据不匹配时,通过黑客代码删除UICollectionViewLayoutAttributes,导致崩溃,我视图中的其他控件放错了位置.

我的收藏内容是"静态的",在加载时加载,因此不可能进行无保护的更改.再次,这与iOS 8完美配合.

所以,我只禁用了此视图的自动布局功能和BINGO!崩溃消失了.Auto-Layout正在使用内部UICollectionSize播放,因此 - (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect方法返回mixup结果.

我试过reloadData,reloadSections,没什么,但是这个工作!

希望通过这种控制可以帮助其他与UIKit异常作斗争的人.


Gul*_*ulz 5

尝试调用layoutIfNeeded()

 cell.collectionView.collectionViewLayout.invalidateLayout()

 cell.collectionView.layoutIfNeeded()

 cell.collectionView.reloadData()
Run Code Online (Sandbox Code Playgroud)

  • 建议不要直接调用layoutSubviews() (4认同)

Edo*_*ier 5

Xcode 8-迅捷3

就我而言,此错误是由自动版式引起的;我有一个嵌入到UIView中的collectionView,我通过将collectionView为空时将其高度设置为0来隐藏它,而当我需要再次显示collectionView时将其高度设置为150。

我设法通过致电删除了该错误

collectionView.collectionViewLayout.invalitdateLayout()

在运行对SuperView上的layoutIfNeeded()调用进行动画处理的代码之前。现在很顺利。

我希望它可以在将来对某人有所帮助。

var sponsoredPlaceSummaries: [PlaceSummary] = [] {

        didSet {

            if sponsoredPlaceSummaries.isEmpty {

                self.sponsoredPlacesViewHeight.constant = 0
                self.collectionView.collectionViewLayout.invalidateLayout()

                UIView.animate(withDuration: 1.0, delay: 0, options: .curveEaseInOut, animations: {
                    self.view.layoutIfNeeded()

                }, completion: nil)

            } else if self.sponsoredPlacesViewHeight.constant != 150 {

                self.sponsoredPlacesViewHeight.constant = 150

                UIView.animate(withDuration: 1.0, delay: 0, options: .curveEaseInOut, animations: {
                    self.view.layoutIfNeeded()

                }, completion: nil)
            }
        } 
Run Code Online (Sandbox Code Playgroud)