是什么导致了这次 iOS 崩溃?UICollectionView 接收到的单元格的布局属性,其索引路径不存在

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27515673/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 04:03:35  来源:igfitidea点击:

What's causing this iOS crash? UICollectionView received layout attributes for a cell with an index path that does not exist

iosobjective-cuicollectionview

提问by Drew

I'm working on an app that has a UICollectionViewController that is crashing in certain mysterious situations that are hard to reproduce. The log for the crash looks like this:

我正在开发一个具有 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}'

Crashes like this only seem to have started occurring in our code once we switched to the iOS 8 SDK.

一旦我们切换到 iOS 8 SDK,像这样的崩溃似乎只在我们的代码中开始发生。

Why is this happening?

为什么会这样?

Note: I already know what the answer to question is, but I found very little information relating to this crash on Stack Overflow and the rest of the web. I'll post the answer below. This bug took my coworker and I three days to track down, so hopefully this post will save someone else a lot of time and frustration. I have filed this bug with Apple.

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

回答by Drew

The crash was happening in the following situation:

崩溃发生在以下情况:

We had a collection view controller that was presenting another view controller on top of it.

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

While the collection view controller was no longer visible, the following sequence of events was occasionally occurring in response to our app's back end requests.

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

  1. [UICollectionView insertItemsAtIndexPaths:]was called with 50 items on the collection view of the hidden UICollectionViewController.
  2. [UICollectionView reloadData]was called on the hidden collection view.
  3. A short delay would occur.
  4. The number of items in the hidden collection view was set to a small number.
  5. [UICollectionView reloadData]was called again.
  6. The view controller was dismissed, revealing the hidden collection view controller.
  1. [UICollectionView insertItemsAtIndexPaths:]在 hidden 的集合视图上调用了 50 个项目UICollectionViewController
  2. [UICollectionView reloadData]在隐藏的集合视图上调用。
  3. 会发生短暂的延迟。
  4. 隐藏集合视图中的项目数设置为一个较小的数字。
  5. [UICollectionView reloadData]又被叫了。
  6. 视图控制器被解散,显示隐藏的集合视图控制器。

The assertion failure in the internal UIKit class UICollectionViewDatawould happen at step 6.

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

So, the lesson is, try to avoid manipulating a collection view that is not visible on the screen.

所以,教训是,尽量避免操作在屏幕上不可见的集合视图。

Our workaround for this problem was to call [UICollectionView reloadSections:]instead of [UICollectionView reloadData]at key points.

我们解决这个问题的方法是调用[UICollectionView reloadSections:]而不是[UICollectionView reloadData]在关键点调用。

We suspect that the effects of reloadDataare deferred to some point in the future, and there are consequently subtle issues with how this may interact with other method calls like insertItemsAtIndexPaths, whereas reloadSectionsis handled immediately, leaving the collection view in a better state.

我们怀疑 的影响reloadData会推迟到未来的某个时间点,因此在如何与其他方法调用(如 )交互方面存在微妙的问题insertItemsAtIndexPaths,而reloadSections立即处理,使集合视图处于更好的状态。

We think that we were not seeing this behavior until we started building our app for iOS 8.

我们认为直到我们开始为 iOS 8 构建我们的应用程序时,我们才看到这种行为。

Sleep well, my friends!

睡个好觉,我的朋友们!

回答by Beuj

For me it was the UICollectionViewLayoutAttribute's array. I'm using it in a UICollectionViewLayoutto store item's attribute.

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

I forgot to empty it in the prepareLayoutmethod.

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

So the layoutAttributesForItemAtIndexPathwas returning incorrect values for indexPath, which led to the same crash.

所以layoutAttributesForItemAtIndexPath返回的 indexPath 值不正确,这导致了同样的崩溃。

With just a removeAllon the array at the beginning of prepareLayout, it's working.

removeAllprepareLayout 开始时数组上只有一个,它就可以工作了。

回答by Ankish Jain

collectionViewLayout caches the attributes. In viewwillappear - Create a new instance of collectionViewLayout and assign it to collectionview.collectionViewLayout In this way all the cached attributes will purge before the reload Your problem might be resolved. Worked for me, especially when you are using other collectionViewLayout libraries.

collectionViewLayout 缓存属性。在 viewwillappear - 创建一个新的 collectionViewLayout 实例并将其分配给 collectionview.collectionViewLayout 这样所有缓存的属性将在重新加载之前清除您的问题可能会得到解决。为我工作,尤其是当您使用其他 collectionViewLayout 库时。

回答by Eric Giguere

I have been working on this same bug for some time and think I've found another source of errors, on iOS 7 but working ok on iOS 8, that causes this same exact error: Auto-Layout!

我一直在研究这个相同的错误一段时间,并认为我发现了另一个错误来源,在 iOS 7 上但在 iOS 8 上工作正常,这会导致同样的错误:自动布局!

I'm using a control that has embedded UICollectionViews in it, a grid. I've noticed by hacking the code to remove the UICollectionViewLayoutAttributes when there is a mismatch with the UICollectionView data, causing the crash, that other controls in my view were misplaced.

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

My collection content is "static", loaded on load and that's it so no possibility for unprotected changes. Again this works perfectly with iOS 8.

我的收藏内容是“静态的”,在加载时加载,因此不可能进行不受保护的更改。这同样适用于 iOS 8。

So, i disabled the Auto-Layout feature for this view only and BINGO! Crash disappeared. The Auto-Layout was playing with the internal UICollectionSize, thus the -(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect method was returning mixup results.

因此,我仅针对此视图和 BINGO 禁用了自动布局功能!崩溃消失了。自动布局正在使用内部 UICollectionSize,因此 -(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect 方法正在返回混合结果。

I tried reloadData, reloadSections, nothing does but this works!

我尝试了 reloadData、reloadSections,但没有任何作用!

Hope it will help someone else fighting with UIKit exception with this control.

希望它能帮助其他人通过这个控件与 UIKit 异常作斗争。

回答by Gulz

try calling layoutIfNeeded()

尝试调用 layoutIfNeeded()

 cell.collectionView.collectionViewLayout.invalidateLayout()

 cell.collectionView.layoutIfNeeded()

 cell.collectionView.reloadData()

回答by Edouard Barbier

Xcode 8 - Swift 3

Xcode 8 - 斯威夫特 3

In my case this bug was caused by Autolayout; I have a collectionView embedded into a UIView that I hide by setting it's height to 0 when the collectionView is empty and back to 150 when I need to show the collectionView again.

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

I managed to remove the bug by calling

我设法通过调用删除了错误

collectionView.collectionViewLayout.invalitdateLayout()

collectionView.collectionViewLayout.invaliddateLayout()

before I run the code that animates the layoutIfNeeded() call on the superView. It's pretty smooth now.

在我运行为 superView 上的 layoutIfNeeded() 调用设置动画的代码之前。现在已经很顺利了。

I hope it can help someone in the future.

我希望它可以帮助将来的某个人。

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)
            }
        } 

回答by m8labs

For me, it was something related to @Drew's answer (my collection view was off screen). I was manipulating a height constraint of the collectionView to collapse it when there is no data. It caused this crash (sometimes!). I put collectionView inside another view and re-assigned @IBOutlet to this new view's height constraint. And made the height of the collectionView to be constant. Crash has gone forever!

对我来说,这与@Drew 的回答有关(我的收藏视图不在屏幕上)。我正在操纵 collectionView 的高度约束以在没有数据时折叠它。它导致了这次崩溃(有时!)。我将 collectionView 放在另一个视图中,并将 @IBOutlet 重新分配给这个新视图的高度约束。并使 collectionView 的高度保持不变。崩溃已经一去不复返了!

回答by timyau

I met the same problem. In my situation, I have 2 UICollectionView in an screen and they have the same size.

我遇到了同样的问题。在我的情况下,我在屏幕中有 2 个 UICollectionView 并且它们的大小相同。

So I reuse the same UICollectionViewLayout as initial parameter <-- That's the problem.

所以我重用相同的 UICollectionViewLayout 作为初始参数 <-- 这就是问题所在。

2 UICollectionView should use different UICollectionViewLayout parameter. So just new another UICollectionViewLayout for second UICollectionView.

2 UICollectionView 应该使用不同的 UICollectionViewLayout 参数。所以只是为第二个 UICollectionView 新建另一个 UICollectionViewLayout。

回答by varunrathi28

Make sure your datasource & delegates for collectionview are connected to correct view controller. I once got this crash ,as i accidently discarded changes in my Main.Storyboard & due to which data source & delegates left disconnected

确保 collectionview 的数据源和委托已连接到正确的视图控制器。我曾经遇到过这个崩溃,因为我不小心丢弃了 Main.Storyboard 中的更改,并且由于哪个数据源和委托断开了连接

回答by Daniel Jones

I was getting the same error, but it was happening in a pretty random fashion, as in I could not recreate the bug consistently. I was using a customLayout where I was setting the attributes for items manually. I would do one thing on my real iPhone that would crash it, but on the xCode simulator it would work. What ended up fixing my problem was using collectionView.reloadData() instead of collectionView.reloadSections([mySectionNum]). I have no idea why it was working sometimes and not others. It seems like it should have crashed all the time, but it was not. Nor, do I have any idea why this fix works.

我遇到了同样的错误,但它以一种非常随机的方式发生,因为我无法始终如一地重新创建错误。我正在使用 customLayout 手动设置项目的属性。我会在我真正的 iPhone 上做一件事会使它崩溃,但在 xCode 模拟器上它会工作。最终解决我的问题的是使用 collectionView.reloadData() 而不是 collectionView.reloadSections([mySectionNum])。我不知道为什么它有时有效而不是其他。看起来它应该一直崩溃,但事实并非如此。也不知道为什么此修复程序有效。