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

问题描述 投票:62回答:16

在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

两个集合视图。

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

有人能给我一些指示吗?

ios uicollectionview ios10 uicollectionviewlayout
16个回答
122
投票

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

这是我的最终代码:

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

1
投票

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

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

0
投票

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


0
投票

我设法通过在调用collectionViewLayout之前重新创建reloadData()来解决这个问题

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


0
投票

这也发生在我身上,但这是因为我的UICollectionViewDataSource改变了,我没有打电话给-[UICollectionView reloadData]。就我而言,我有以下数据结构:

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

我有两个UICollectionViews:一个用于Foos,一个用于Bars。在我的-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;
  }
}

没有-reloadData调用,我会在旋转设备时看到崩溃。


0
投票

花了两天时间后,下面的代码解决了这个问题。在加载集合视图之前,请编写以下代码。

let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
collecView.collectionViewLayout = flowLayout 
collecView.delegate = self
collecView.dataSource = self
collecView.reloadData()

然后崩溃将被解决,但您会在集合视图单元格中发现问题,因为单元格是按照您的设计压缩或不压缩。然后在scrolldirection行之后只有一行代码

flowLayout.itemSize = CGSize(width: 92, height: 100)

使用上面的代码行,您可以调整集合视图单元格布局。

我希望它能帮助别人。


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
}

希望它可以帮助某人


0
投票

以下是我的意思:

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

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


43
投票

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

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

17
投票

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

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

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


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

5
投票

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

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

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


4
投票

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

3
投票

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

collectionView.dataSource = nil

collectionView.delegate = nil

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

collectionView.dataSource = self

collectionView.delegate = self

collectionView.reloadData()

3
投票

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

self.collectionViewContacts.collectionViewLayout.invalidateLayout()

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


            self.collectionViewContacts.reloadData()

解决了这个问题


2
投票

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

let layout = UICollectionViewFlowLayout()
collectionView?.collectionViewLayout = layout
© www.soinside.com 2019 - 2024. All rights reserved.