iOS 10 bug: UICollectionView received layout attri

2019-01-10 05:45发布

Running my app in a device with iOS 10 I get this error:

UICollectionView received layout attributes for a cell with an index path that does not exist

In iOS 8 and 9 works fine. I have been researching and I have found that is something related to invalidate the collection view layout. I tried to implement that solution with no success, so I would like to ask for direct help. This is my hierarchy view:

->Table view 
    ->Each cell of table is a custom collection view [GitHub Repo][1]
        ->Each item of collection view has another collection view

What I have tried is to insert

    [self.collectionView.collectionViewLayout invalidateLayout];

In the

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView

of both collection views.

Also I have tried to invalidate layout before doing a reload data, does not work...

Could anyone give me some directions to take?

14条回答
▲ chillily
2楼-- · 2019-01-10 06:20

Previous answer helps, but if you use autoresizing cells, their size will be incorrect.

 UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
 layout.estimatedItemSize = CGSizeMake(60, 25);
 layout.itemSize = UICollectionViewFlowLayoutAutomaticSize;             
 self.collectionView.collectionViewLayout = layout;

I solve this issues by replacing

[self.collectionView reloadData];

to

[self.collectionView reloadSections:indexSet];
查看更多
仙女界的扛把子
3楼-- · 2019-01-10 06:20

The @Katrin's answer helped a lot, but I could achieve even better results by adding one more line:

collectionView.reloadData()
collectionView.collectionViewLayout.invalidateLayout()
collectionView.layoutSubviews() // <-- here it is :)

I can't now say if I could reproduce crash with this line or not, but I guess there was one... So, still not a silver bullet, but something.

查看更多
闹够了就滚
4楼-- · 2019-01-10 06:21

Calling invalidateLayout did not prevent the crash in my case. (It worked if the number of items in the collection view increased but not if it decreased). Context: I have a UICollectionView inside a UITableViewCell and when the table cell is re-used I reset the delegates of the collectionView. I fixed the problem not by invalidating the cache but by RECREATING the layout object any time I reset the delegate, then calling reloadData():

foo.dataSource = newDataSource
foo.delegate = newDelegate
foo.fixLayoutBug()
foo.reloadData()

func fixLayoutBug() {
    let oldLayout = collectionViewLayout as! UICollectionViewFlowLayout
    let newLayout = UICollectionViewFlowLayout()
    newLayout.estimatedItemSize = oldLayout.estimatedItemSize
    newLayout.footerReferenceSize = oldLayout.footerReferenceSize
    newLayout.headerReferenceSize = oldLayout.headerReferenceSize
    newLayout.itemSize = oldLayout.itemSize
    newLayout.minimumInteritemSpacing = oldLayout.minimumInteritemSpacing
    newLayout.minimumLineSpacing = oldLayout.minimumLineSpacing
    newLayout.scrollDirection = oldLayout.scrollDirection
    newLayout.sectionFootersPinToVisibleBounds = oldLayout.sectionFootersPinToVisibleBounds
    newLayout.sectionHeadersPinToVisibleBounds = oldLayout.sectionHeadersPinToVisibleBounds
    newLayout.sectionInset = oldLayout.sectionInset
    newLayout.sectionInsetReference = oldLayout.sectionInsetReference
    collectionViewLayout = newLayout
}
查看更多
Evening l夕情丶
5楼-- · 2019-01-10 06:22

This happened to me as well, but it was because my UICollectionViewDataSource changed, and I didn't call -[UICollectionView reloadData]. In my case, I had the following data structure:

struct Bar { let name: String }
struct Foo { let name: String; let bars: [Bar] }

I had two UICollectionViews: one for Foos and one for Bars. In my -collectionView:didSelectItemAtIndexPath:, I had the following:

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
  if (collectionView == self.fooCollectionView) {
    self.selectedFoo = self.foos[indexPath.row];
    [self.barCollectionView reloadData];
    self.fooCollectionView.hidden = YES;
    self.barCollectionView.hidden = NO;
  } else if (collectionView == self.barCollectionView) {
    // do some stuff with the selected bar

    self.selectedFoo = nil;
    // This -reloadData call fixed my error. I thought I didn't
    // need it since my UICollectionView was hidden
    [self.barCollectionView reloadData];
    self.barCollectionView.hidden = YES;
    self.fooCollectionView.hidden = NO;
  }
}

Without the -reloadData call, I would see the crash when I rotated the device.

查看更多
我欲成王,谁敢阻挡
6楼-- · 2019-01-10 06:23

This happened to me when number of cells in collectionView changed. Turns out I was missing invalidateLayout after calling reloadData. After adding it, I haven't experienced any more crashes. Apple has made some modifications to collectionViews in iOS10. I guess that's the reason why we are not experiencing same problem on older versions.

Here's my final code:

[self.collectionView reloadData];
[self.collectionView.collectionViewLayout invalidateLayout];
查看更多
太酷不给撩
7楼-- · 2019-01-10 06:24

It's not the best to reloadData everytime (You should use insertItems and deleteItems, and even reloadSections). But... after saying that in some cases it's a valid so, you can actually do this:

collectionView.dataSource = nil

collectionView.delegate = nil

/*... All changes here. ... */

collectionView.dataSource = self

collectionView.delegate = self

collectionView.reloadData()
查看更多
登录 后发表回答