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

匿名 (未验证) 提交于 2019-12-03 02:45:02

问题:

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:

*** 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.

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.

回答1:

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.

The assertion failure in the internal UIKit class UICollectionViewData would happen at step 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.

We suspect that the effects of reloadData are 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 reloadSections is handled immediately, leaving the collection view in a better state.

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

Sleep well, my friends!



回答2:

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

I forgot to empty it in the prepareLayout method.

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

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



回答3:

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!

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.

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.

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.

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

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



回答4:

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.



回答5:

this helped me:

cell.collectionView.collectionViewLayout.invalidateLayout() cell.collectionView.reloadData() cell.collectionView.layoutSubviews() 


回答6:

Xcode 8 - Swift 3

In my case this bug was caused by Autolayout; I have a collectionView embedded into a UIView that a 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.

I managed to remove the bug by calling

collectionView.collectionViewLayout.invalitdateLayout()

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

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


回答7:

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

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

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



回答8:

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!



回答9:

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



回答10:

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.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!