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


68

在装有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

这两个集合视图。

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

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


确保UICollectionViewLayoutAttributes的indexPath与UICollectionViewCell的indexPath相匹配
onmyway133

Answers:


141

当collectionView中的单元格数量发生更改时,这发生在我身上。原来,我在调用reloadData之后丢失了invalidateLayout。添加之后,我再也没有崩溃过。苹果对iOS10中的collectionViews进行了一些修改。我想这就是为什么我们在旧版本上没有遇到相同问题的原因。

这是我的最终代码:

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

//Swift 4.2 Version
collectionView.reloadData()
collectionView.collectionViewLayout.invalidateLayout()

3
它有帮助,谢谢。但是没有人知道,为什么有帮助?
维尔姆·库尔兹

4
当collectionview行数增加时,它起作用。但是当行数减少时再次崩溃(即,当5列到10列时工作正常,而10列到5列时崩溃)。
杰·马尤

2
我遇到的与@JayMayu相同。计数下降时我崩溃了。
Josh Hrach

4
@JayMayu我尝试过,但是对于我的应用程序中的某些视图控制器仍然崩溃。感谢您的帮助。我很感激。
Josh Hrach

3
就我而言,我在1个tableview单元中使用2个collectionviews。因此,为每个人分配单独的UICollectionViewFlowLayout可以解决此奇怪的崩溃。
user1994956 '17

48

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

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

解决了我的问题!谢谢!
DaniSDE

帮助我进行自定义布局!谢谢
Codenator81 '18

@DaniSDE您能解释一下如何/在哪里使用吗?谢谢
user6520705 '18

2
@ user6520705在布局类中,您应该已覆盖prepare函数,在其中填充[UICollectionViewLayoutAttributes]数组。此数组是布局属性的缓存。只需在此准备方法的开始将其清除即可。
ArturC

1
这次真是万分感谢!我尝试了其他答案中的所有invalidateLayout建议,但没有成功。这已修复。我有一个collectionView.performBatchUpdates封闭的地方,我要从我的收藏中删除一个项目,并得到问题中提到的错误。我一直在我的prepare方法中创建一个缓存,并从中删除所有项目。谢谢谢谢!(使用Swift 5和iOS13)
NW

19

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

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

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


谢谢,它为我工作。我从几天开始就陷入其中。您真棒:)
Khushbu Desai

嘿,我遇到一些问题,我有两个集合视图,一个是cltnTools,另一个是cltnEdits。每当用户单击来自cltnTools的任何单元格时,都会出现cltnEdits,原因是我遇到错误:'UICollectionView收到了索引路径不存在的单元格的布局属性:<NSIndexPath:0xb141c60> {length = 2,path = 0- 2}
Khushbu Desai

这个问题是使用l​​et layout = UICollectionViewFlowLayout()cltnEdits.collectionViewLayout = layout来解决的,每件事都可以正常工作,但是我的cltnEdits单元格是垂直滚动的,但是我想要两个collectionvie的水平滚动,所以我写了这行代码layout.scrollDirection = .horizo​​ntal水平滚动,然后我再次面对该错误。可以帮我吗?我该如何解决。
库舒德赛

@KhushbuDesai我认为问题不在于水平/垂直设置布局,在配置collectionview的布局或重新加载数据时,您可能在其他地方遇到了问题。我需要更多代码来找
出问题


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

6

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

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

我现在不能说是否可以用此行重现崩溃,但我想那是一个……所以,仍然不是灵丹妙药,而是某些东西。


5
实际上不是一个好的建议。您不应该layoutSubviews()直接打电话。如果要强制更新布局,请setNeedsLayout()在下一次图形更新之前调用该方法。如果要立即更新视图的布局,请调用该layoutIfNeeded()方法。 developer.apple.com/documentation/uikit/uiview/...
阿尔乔姆基里洛夫

@ArtKirillov是的,但是无论如何它都是hack,否则您将发生崩溃。我已经不记得了,但是layoutIfNeeded()可能不起作用。
m8labs

4

重置UICollectionView的布局可以为我解决问题:

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

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

我有相同的错误和设置,dataSourcedelegatenil解决该错误。就我而言,我使用UICollectionView两个不同的 UICollectionViewLayouts,我认为回收过程存在问题。谢谢。
Mu Sa

3

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

self.collectionViewContacts.collectionViewLayout.invalidateLayout()

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


            self.collectionViewContacts.reloadData()

解决了这个问题


2

我在使用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)

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

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

1

如果要保持滚动位置并修复崩溃,可以使用以下命令:

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

为什么我的回答被否决?我知道这有帮助的事实
Mati Bot's

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

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

经过两天的时间,以下代码解决了该问题。在加载collectionview之前,请编写以下代码。

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

然后可以解决崩溃问题,但是您会在collectionview单元中发现问题,因为这些单元是否按照您的设计进行了压缩。然后滚动方向行之后只有一行代码

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

使用上面的代码行,您可以调整collectionview单元的布局。

希望对您有所帮助。


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在重新加载数据之前滚动到第一个单元格,这似乎是导致我的错误的原因。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.