web-dev-qa-db-ja.com

iOS 10のバグ:UICollectionViewは、存在しないインデックスパスを持つセルのレイアウト属性を受け取りました

IOS 10を搭載したデバイスでアプリを実行すると、次のエラーが表示されます。

ICollectionViewは、存在しないインデックスパスを持つセルのレイアウト属性を受け取りました

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

両方のコレクションビューの。

また、データをリロードする前にレイアウトを無効にしようとしましたが、動作しません...

誰かが私に取る方向を教えてもらえますか?

63
cmacera

これは、collectionViewのセルの数が変更されたときに起こりました。 reloadDataを呼び出した後、invalidateLayoutが見つかりませんでした。追加した後、これ以上クラッシュすることはありません。 Appleは、iOS10のcollectionViewsにいくつかの変更を加えました。これが、古いバージョンで同じ問題が発生していない理由だと思います。

最終的なコードは次のとおりです。

[self.collectionView reloadData];
[self.collectionView.collectionViewLayout invalidateLayout];
125
Katrin Annuk

カスタムレイアウトがある場合は、キャッシュの消去(UICollectionViewLayoutAttributes)を忘れずに準備関数をオーバーライドしてください

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

両方のコレクションに同じUICollectionViewFlowLayoutを追加したため、同じビューの2つのcollectionViewsでこの問題に直面しました。

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

もちろん、collectionView1データが変更されると、データのリロード時にクラッシュします。これが誰かを助けることができるなら。

17
Aximem

前の答えが役立ちますが、自動サイズ変更セルを使用すると、セルのサイズが正しくなくなります。

 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
Andrey

@Katrinの答えは大いに役立ちましたが、もう1行追加することでさらに良い結果を得ることができました。

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

この行でクラッシュを再現できるかどうかは今は言えませんが、1つあったと思います。

5
m8labs

私の場合、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
}
4
Nick Kallen

私の場合、NSlayoutConstraint定数を変更していました

self.collectionViewContacts.collectionViewLayout.invalidateLayout()

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


            self.collectionViewContacts.reloadData()

問題を解決しました

3
jeff ayan

ReloadDataを毎回リロードするのは最適ではありません(insertItemsとdeleteItems、さらにreloadSectionsを使用する必要があります)。しかし...場合によっては有効であると言った後、実際にこれを行うことができます:

collectionView.dataSource = nil

collectionView.delegate = nil

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

collectionView.dataSource = self

collectionView.delegate = self

collectionView.reloadData()
3
Kevin Bel

UICollectionViewのレイアウトをリセットすると、問題が解決しました。

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

RxDataSources with RxCollectionViewSectionedAnimatedDataSourceを使用して問題が発生し、2つの回答を組み合わせて解決しました。コレクションをリアクティブにリロードする場合、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)

prepare()レイアウトメソッドでcache配列をクリアします。コードは次のとおりです。

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

スクロール位置を維持してクラッシュを修正したい場合は、これを使用できます:

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

私の場合、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
Gulfam Khan

以下は私のためにそれを作りました:

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

このコードは、データをリロードする前に、collectionViewを強制的に最初のセルにスクロールさせているようです。

0
Javier Alzueta

これは私にも起こりましたが、それは私のUICollectionViewDataSourceが変更され、-[UICollectionView reloadData]を呼び出さなかったためです。私の場合、次のデータ構造がありました。

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

2つのUICollectionViewsがありました。1つはFoos用で、もう1つは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
Heath Borders

私もこの問題を抱えていました。私の場合、カスタムUICollectionViewLayoutがコレクションビューに適用されていました。問題は、表示するセル数の古いデータがあったことです。 UICollectionView received layout attributes for a cell with an index path that does not existが表示されたときに確認できることは間違いありません

0
Trev14

アニメーション化された制約の変更を使用してコレクションビューを非表示にしているときに、同様の問題に直面しました。私のソリューションは、既存の回答に基づいています。

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
dimazava

2日を費やした後、次のコードで問題を解決しました。コレクションビューを読み込む前に、次のコードを記述します。

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

その後、クラッシュは解決されますが、セルが設計どおりに圧縮されているかどうかによって、collectionviewセルに問題が見つかります。次に、scrolldirection lineの直後のコード行

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

上記のコード行を使用すると、collectionviewセルのレイアウトを調整できます。

私はそれが誰かを助けることを願っています。

0
Arshad Shaik

これを解決するには、reloadData()を呼び出す前にcollectionViewLayoutを再作成しました。

この問題は、おそらくdataSource(つまり、collectionViewを保持するView Controllerではない)の個別のインスタンスに関連しており、データソースをスワップするとクラッシュが発生する可能性がありました。

0
ullstrm