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
両方のコレクションビューの。
また、データをリロードする前にレイアウトを無効にしようとしましたが、動作しません...
誰かが私に取る方向を教えてもらえますか?
これは、collectionViewのセルの数が変更されたときに起こりました。 reloadDataを呼び出した後、invalidateLayoutが見つかりませんでした。追加した後、これ以上クラッシュすることはありません。 Appleは、iOS10のcollectionViewsにいくつかの変更を加えました。これが、古いバージョンで同じ問題が発生していない理由だと思います。
最終的なコードは次のとおりです。
[self.collectionView reloadData];
[self.collectionView.collectionViewLayout invalidateLayout];
カスタムレイアウトがある場合は、キャッシュの消去(UICollectionViewLayoutAttributes)を忘れずに準備関数をオーバーライドしてください
override func prepare() {
super.prepare()
cache.removeAll()
}
両方のコレクションに同じUICollectionViewFlowLayoutを追加したため、同じビューの2つのcollectionViewsでこの問題に直面しました。
let layout = UICollectionViewFlowLayout()
collectionView1.collectionViewLayout = layout
collectionView2.collectionViewLayout = layout
もちろん、collectionView1データが変更されると、データのリロード時にクラッシュします。これが誰かを助けることができるなら。
前の答えが役立ちますが、自動サイズ変更セルを使用すると、セルのサイズが正しくなくなります。
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.estimatedItemSize = CGSizeMake(60, 25);
layout.itemSize = UICollectionViewFlowLayoutAutomaticSize;
self.collectionView.collectionViewLayout = layout;
交換することでこの問題を解決します
[self.collectionView reloadData];
に
[self.collectionView reloadSections:indexSet];
@Katrinの答えは大いに役立ちましたが、もう1行追加することでさらに良い結果を得ることができました。
collectionView.reloadData()
collectionView.collectionViewLayout.invalidateLayout()
collectionView.layoutSubviews() // <-- here it is :)
この行でクラッシュを再現できるかどうかは今は言えませんが、1つあったと思います。
私の場合、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
}
私の場合、NSlayoutConstraint定数を変更していました
self.collectionViewContacts.collectionViewLayout.invalidateLayout()
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
self.collectionViewContacts.reloadData()
問題を解決しました
ReloadDataを毎回リロードするのは最適ではありません(insertItemsとdeleteItems、さらにreloadSectionsを使用する必要があります)。しかし...場合によっては有効であると言った後、実際にこれを行うことができます:
collectionView.dataSource = nil
collectionView.delegate = nil
/*... All changes here. ... */
collectionView.dataSource = self
collectionView.delegate = self
collectionView.reloadData()
UICollectionView
のレイアウトをリセットすると、問題が解決しました。
let layout = UICollectionViewFlowLayout()
collectionView?.collectionViewLayout = layout
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
}
...
スクロール位置を維持してクラッシュを修正したい場合は、これを使用できます:
collectionView.reloadData()
let context = collectionView.collectionViewLayout.invalidationContext(forBoundsChange: collectionView.bounds)
context.contentOffsetAdjustment = CGPoint.zero
collectionView.collectionViewLayout.invalidateLayout(with: context)
私の場合、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
}
それが誰かを助けることを願っています
以下は私のためにそれを作りました:
[self.collectionView reloadData];
[self.collectionView layoutIfNeeded];
このコードは、データをリロードする前に、collectionViewを強制的に最初のセルにスクロールさせているようです。
これは私にも起こりましたが、それは私のUICollectionViewDataSource
が変更され、-[UICollectionView reloadData]
を呼び出さなかったためです。私の場合、次のデータ構造がありました。
struct Bar { let name: String }
struct Foo { let name: String; let bars: [Bar] }
2つのUICollectionView
sがありました。1つはFoo
s用で、もう1つはBar
s用です。 -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
呼び出しがないと、デバイスを回転させたときにクラッシュが発生します。
私もこの問題を抱えていました。私の場合、カスタムUICollectionViewLayout
がコレクションビューに適用されていました。問題は、表示するセル数の古いデータがあったことです。 UICollectionView received layout attributes for a cell with an index path that does not exist
が表示されたときに確認できることは間違いありません
アニメーション化された制約の変更を使用してコレクションビューを非表示にしているときに、同様の問題に直面しました。私のソリューションは、既存の回答に基づいています。
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()
}
})
}
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セルのレイアウトを調整できます。
私はそれが誰かを助けることを願っています。
これを解決するには、reloadData()
を呼び出す前にcollectionViewLayout
を再作成しました。
この問題は、おそらくdataSource
(つまり、collectionViewを保持するView Controllerではない)の個別のインスタンスに関連しており、データソースをスワップするとクラッシュが発生する可能性がありました。