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

时间:2016-10-05 06:58:35

标签: ios uicollectionview ios10 uicollectionviewlayout

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

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

在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

我试过的是插入

    [self.collectionView.collectionViewLayout invalidateLayout];

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
两个集合视图中的

此外,我在尝试重新加载数据之前尝试使布局无效,不起作用......

有人可以给我一些指示吗?

17 个答案:

答案 0 :(得分:121)

当collectionView中的单元格数量发生变化时,就发生了这种情况。原来我在调用reloadData后缺少invalidateLayout。添加后,我没有遇到任何更多的崩溃。 Apple已经对iOS10中的collectionView进行了一些修改。我猜这就是我们在旧版本上没有遇到同样问题的原因。

这是我的最终代码:

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

答案 1 :(得分:42)

如果您有自定义布局,请记住在覆盖prepare func时清除缓存(UICollectionViewLayoutAttributes)

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

答案 2 :(得分:17)

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

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

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

答案 3 :(得分:6)

以前的回答会有所帮助,但如果您使用自动调整单元格,则其大小将不正确。

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

我通过替换

解决了这个问题
[self.collectionView reloadData];

[self.collectionView reloadSections:indexSet];

答案 4 :(得分:5)

@ Katrin的回答帮了很多忙,但我可以通过增加一行来取得更好的成绩:

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

我现在不能说我是否可以用这条线重现崩溃,但我猜有一条......所以,仍然不是一颗银弹,而是一些东西。

答案 5 :(得分:3)

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

self.collectionViewContacts.collectionViewLayout.invalidateLayout()

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


            self.collectionViewContacts.reloadData()

解决了问题

答案 6 :(得分:3)

在我的情况下,致电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
}

答案 7 :(得分:3)

每次reloadData都不是最好的(你应该使用insertItems和deleteItems,甚至reloadSections)。 但是......在说某些情况下它是有效的之后,你实际上可以做到这一点:

collectionView.dataSource = nil

collectionView.delegate = nil

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

collectionView.dataSource = self

collectionView.delegate = self

collectionView.reloadData()

答案 8 :(得分:2)

重置UICollectionView的布局为我解决了这个问题:

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

答案 9 :(得分:1)

如果您想保持滚动位置并修复崩溃,可以使用:

collectionView.reloadData()
let context = collectionView.collectionViewLayout.invalidationContext(forBoundsChange: collectionView.bounds)
context.contentOffsetAdjustment = CGPoint.zero
collectionView.collectionViewLayout.invalidateLayout(with: context)

答案 10 :(得分:1)

我在将RxDataSourcesRxCollectionViewSectionedAnimatedDataSource一起使用时遇到了问题,并通过组合两个答案解决了该问题。反应性地重新加载我的收藏集时,我必须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)

,并以cache布局方法清除prepare()数组。这是代码:

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

答案 11 :(得分:1)

在使用动画约束更改显示隐藏集合视图时,我遇到了类似的问题。我的解决方案基于现有的答案。

self.filtersCollectionView.reloadData()
if isNeedToUpdateConstraint {
    filtersCollectionViewHeightLayoutConstraint.constant = updatedHeight
    UIView.animate(withDuration: 0.1, animations: {
        self.view.setNeedsLayout()
    }, completion: { completion in
        if completion {
            self.filtersCollectionView.collectionViewLayout.invalidateLayout()
        }
    })
}

答案 12 :(得分:0)

我也有这个问题。在我的情况下,有一个自定义UICollectionViewLayout应用于集合视图&amp;问题是它有应该显示的单元格数量的陈旧数据。当你看到UICollectionView received layout attributes for a cell with an index path that does not exist

时,这肯定是你可以检查的

答案 13 :(得分:0)

我设法通过在调用collectionViewLayout

之前重新创建reloadData()来解决此问题

问题可能与我有dataSource的单独实例(即不是持有collectionView的视图控制器)有关,而且在交换数据源时,可能会出现崩溃。

答案 14 :(得分:0)

这也发生在我身上,但这是因为我的The name 'InitializeComponent' does not exist in the current context发生了变化,我没有打电话给UICollectionViewDataSource。就我而言,我有以下数据结构:

-[UICollectionView reloadData]

我有两个struct Bar { let name: String } struct Foo { let name: String; let bars: [Bar] } s:一个用于UICollectionView个,一个用于Foo个。在我的Bar中,我有以下内容:

-collectionView:didSelectItemAtIndexPath:

如果没有- (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; } } 调用,我会在旋转设备时看到崩溃。

答案 15 :(得分:0)

就我而言,我正在更改NSLayoutConstraint的常量,上述解决方案均不适用于我。感谢@jeff ayan的回答给了我提示。我用这段代码解决了问题

override func prepareForReuse() {
    super.prepareForReuse()
    imagesCollectionHeight.constant = 100 // changing collectionview height constant
    imageContainerWidth.constant = 100  //changing width constant of some view
    self.layoutIfNeeded() // this line did the trick for me
}

希望它对某人有帮助

答案 16 :(得分:0)

以下内容适合我:

[self.collectionView reloadData];
[self.collectionView layoutIfNeeded];

这段代码似乎迫使collectionView在重新加载数据之前滚动到第一个单元格,这似乎是导致我的错误的原因。