iOS 10 错误:UICollectionView 收到了索引路径不存在的单元格的布局属性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39867325/
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 10:28:17  来源:igfitidea点击:

iOS 10 bug: UICollectionView received layout attributes for a cell with an index path that does not exist

iosuicollectionviewios10uicollectionviewlayout

提问by cmacera

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

在装有 iOS 10 的设备上运行我的应用程序时出现此错误:

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

UICollectionView 接收到的单元格的布局属性,其索引路径不存在

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:

在 iOS 8 和 9 中工作正常。我一直在研究,我发现这与使集合视图布局无效有关。我试图实施该解决方案但没有成功,所以我想寻求直接帮助。这是我的层次结构视图:

->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?

谁能给我一些方向?

回答by Katrin Annuk

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.

当 collectionView 中的单元格数量发生变化时,这发生在我身上。结果我在调用 reloadData 后丢失了 invalidateLayout。添加它后,我没有遇到任何崩溃。苹果对 iOS10 中的 collectionViews 做了一些修改。我想这就是为什么我们在旧版本上没有遇到同样问题的原因。

Here's my final code:

这是我的最终代码:

[self.collectionView reloadData];
[self.collectionView.collectionViewLayout invalidateLayout];

回答by ArturC

When you have custom layout, remember to clear cache (UICollectionViewLayoutAttributes) while overriding prepare func

当你有自定义布局时,记得在覆盖 prepare func 时清除缓存 (UICollectionViewLayoutAttributes)

    override func prepare() {
       super.prepare()
       cache.removeAll()
    }

回答by Aximem

I also faced this issue with 2 collectionViews in the same view because I added the same UICollectionViewFlowLayout in both collections :

我在同一个视图中也遇到了 2 个 collectionViews 的这个问题,因为我在两个集合中添加了相同的 UICollectionViewFlowLayout :

let layout = UICollectionViewFlowLayout()
collectionView1.collectionViewLayout = layout
collectionView2.collectionViewLayout = layout

Of course it crashes on reload data if collectionView1 Data change. If this could help someone.

当然,如果 collectionView1 数据发生变化,它会在重新加载数据时崩溃。如果这可以帮助某人。

回答by Andrey

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];

回答by m8labs

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

@Katrin 的回答很有帮助,但我可以通过再添加一行来获得更好的结果:

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.

我现在不能说我是否可以用这条线重现崩溃,但我想有一个......所以,仍然不是灵丹妙药,而是一些东西。

回答by krlbsk

Resetting UICollectionView's layout solved the problem for me:

ResetUICollectionView的布局为我解决了这个问题:

let layout = UICollectionViewFlowLayout()
collectionView?.collectionViewLayout = layout

回答by Nick Kallen

Calling invalidateLayoutdid 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():

invalidateLayout在我的情况下,呼叫并没有阻止崩溃。(如果集合视图中的项目数量增加则有效,但如果减少则无效)。上下文:我在 UITableViewCell 中有一个 UICollectionView,当重新使用表格单元格时,我重置了 collectionView 的委托。我不是通过使缓存无效而是通过在重置委托时重新创建布局对象来解决问题,然后调用 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
}

回答by jeff ayan

In my case, I was changing the NSlayoutConstraint constant

就我而言,我正在更改 NSlayoutConstraint 常量

self.collectionViewContacts.collectionViewLayout.invalidateLayout()

            UIView.animate(withDuration: 0.3) {
                self.view.layoutIfNeeded()
            }


            self.collectionViewContacts.reloadData()

solved the issue

解决了这个问题

回答by Kevin Bel

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:

每次都重新加载数据并不是最好的(您应该使用 insertItems 和 deleteItems,甚至是 reloadSections)。但是......在说在某些情况下它是有效的之后,你实际上可以这样做:

collectionView.dataSource = nil

collectionView.delegate = nil

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

collectionView.dataSource = self

collectionView.delegate = self

collectionView.reloadData()

回答by Mol0ko

I had the problem using RxDataSourceswith RxCollectionViewSectionedAnimatedDataSourceand solved it by combining two answers. I have to invalidateLayout()when reactively reload my collection:

我在使用RxDataSources时遇到了问题,RxCollectionViewSectionedAnimatedDataSource并通过结合两个答案来解决它。invalidateLayout()当我被动地重新加载我的收藏时,我必须这样做:

    ...
    .asDriver(onErrorJustReturn: [])
    .do(onNext: { [weak self] _ in
        DispatchQueue.main.async {
            self?.collectionViewLayout.invalidateLayout()
            self?.collectionView.layoutSubviews()
        }
    }).drive(collectionView.rx.items(dataSource: collectionController.dataSource))
    .disposed(by: disposeBag)

and clear cachearray(s) in prepare()layout method. Here is the code:

cacheprepare()布局方法中清除数组。这是代码:

override func prepare() {
    super.prepare()
    cache.removeAll()
    guard let collectionView = collectionView,
        collectionView.numberOfSections > 0,
        let delegate = delegate else {
        return
    }
    ...