CustomLayoutを使用してCollectionViewでビューを実行しました。 iOS6ではうまく機能しましたが、iOS7ではこのような例外がスローされます。
キャッチされない例外「NSInternalInconsistencyException」が原因でアプリを終了します、理由:
'インデックスパス({長さ= 2、パス= 0-0})の補足アイテムのレイアウト属性がCustomSupplementaryAttributesから変更されました:0xd1123a0インデックスパス:(NSIndexPath:0xd112580 {長さ= 2、パス= 0-0});要素の種類:(識別子);フレーム=(0 0; 1135.66 45); zIndex = -1; CustomSupplementaryAttributes:0xd583c80インデックスパス:(NSIndexPath:0xd583c70 {length = 2、path = 0-0});要素の種類:(識別子);フレーム=(0 0; 1135.66 45); zIndex = -1;レイアウトを無効にすることなく」
更新する前に既存のレイアウトを無効にする必要があります。エラーメッセージの最後を参照してください。
レイアウトを無効にすることなく」
[collectionViewLayout invalidateLayout];
IOS 10で新機能が導入されました。これはCell Prefetching
。 SupplementaryView
の動的な位置がクラッシュします。古い動作で実行するには、prefetchingEnabled
を無効にする必要があります。 iOS 10のデフォルトはtrue
です。
// Obj-C
// This function is available in iOS 10. Disable it for dynamic position of `SupplementaryView `.
if ([self.collectionView respondsToSelector:@selector(setPrefetchingEnabled:)]) {
self.collectionView.prefetchingEnabled = false;
}
// Swift
if #available(iOS 10, *) {
// Thanks @maksa
collectionView.prefetchingEnabled = false
// Swift 3 style
colView.isPrefetchingEnabled = false
}
この問題は嫌いです。私はこの問題に2日間費やしています。 Cell Pre-fetch @iOS 1 に関するリファレンス。
@ Away Linが正しい。 。そのデリゲートメソッドを実装することで同じ問題を解決します。
ぼくの Custom UICollectionViewLayout
はlayoutAttributesForElementsInRect
の属性を変更します。セクションの位置は静的ではなく動的です。ですから、layout attributes for supplementary item at index path ... changed from ... to ...
。変更する前に、invalideLayout
関連メソッドを呼び出す必要があります。
そして、このデリゲートメソッドを実装してtrue
を返すと、メソッドinvalidateLayoutWithContext:
は、UICollectionViewLayout
をスクロールするときに呼び出されます。デフォルトでは、false
を返します。
- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}
戻り値コレクションビューでレイアウトの更新が必要な場合はtrue、レイアウトを変更する必要がない場合はfalse。
説明このメソッドのデフォルトの実装はfalseを返します。サブクラスはそれをオーバーライドし、コレクションビューの境界の変更でセルおよび補足ビューのレイアウトの変更が必要かどうかに基づいて、適切な値を返すことができます。
コレクションビューの境界が変更され、このメソッドがtrueを返す場合、コレクションビューはinvalidateLayoutWithContext:メソッドを呼び出してレイアウトを無効にします。
IOS 6.0以降で利用できます。
同じ例外がありました。iOS7では、Appleのドキュメント here に記載されているように、UICollectionViewLayoutAttributesサブクラスの継承されたisEqual:
をオーバーライドする必要があります。
UICollectionViewFlowLayout
のサブクラスでメソッドをオーバーライドすることで問題を解決しました:
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBound
yESを返す
方法や理由は完全にはわかりませんが、これはiOS 12で修正されているようで、補助的なビューのサイズ変更とプリフェッチの両方をサポートしています。私にとってのトリックは、物事が正しい順序で起こっていることを確認することでした。
以下は、伸縮可能なヘッダービューの実際の実装です。 layoutAttributesForElements(in rect: CGRect)
で発生するヘッダーのサイズ変更の実装に注意してください。
class StretchyHeaderLayout: UICollectionViewFlowLayout {
var cache = [UICollectionViewLayoutAttributes]()
override func prepare() {
super.prepare()
cache.removeAll()
guard let collectionView = collectionView else { return }
let sections = [Int](0..<collectionView.numberOfSections)
for section in sections {
let items = [Int](0..<collectionView.numberOfItems(inSection: section))
for item in items {
let indexPath = IndexPath(item: item, section: section)
if let attribute = layoutAttributesForItem(at: indexPath) {
cache.append(attribute)
}
}
}
if let header = layoutAttributesForSupplementaryView(ofKind: StretchyCollectionHeaderKind, at: IndexPath(item: 0, section: 0)) {
cache.append(header)
}
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let visibleAttributes = cache.filter { rect.contains($0.frame) || rect.intersects($0.frame) }
guard let collectionView = collectionView else { return visibleAttributes }
// Find the header and stretch it while scrolling.
guard let header = visibleAttributes.filter({ $0.representedElementKind == StretchyCollectionHeaderKind }).first else { return visibleAttributes }
header.frame.Origin.y = collectionView.contentOffset.y
header.frame.size.height = headerHeight.home - collectionView.contentOffset.y
header.frame.size.width = collectionView.frame.size.width
return visibleAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attributes = super.layoutAttributesForItem(at: indexPath as IndexPath)?.copy() as! UICollectionViewLayoutAttributes
guard collectionView != nil else { return attributes }
attributes.frame.Origin.y = headerHeight.home + attributes.frame.Origin.y
return attributes
}
override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: StretchyCollectionHeaderKind, with: indexPath)
}
override var collectionViewContentSize: CGSize {
get {
guard let collectionView = collectionView else { return .zero }
let numberOfSections = collectionView.numberOfSections
let lastSection = numberOfSections - 1
let numberOfItems = collectionView.numberOfItems(inSection: lastSection)
let lastItem = numberOfItems - 1
guard let lastCell = layoutAttributesForItem(at: IndexPath(item: lastItem, section: lastSection)) else { return .zero }
return CGSize(width: collectionView.frame.width, height: lastCell.frame.maxY + sectionInset.bottom)
}
}
}
PS:この時点では、キャッシュが実際には何の目的も果たしていないことを認識しています:)
コレクションビューのコンテンツサイズに依存するコードがあったため、この問題も発生しました。私のコードは、collectionViewContentSize
ではなくcollectionView!.contentSize
を介してコンテンツサイズにアクセスしていました。
前者はcollectionView
のUICollectionViewLayout
プロパティを使用し、後者はカスタム実装されたレイアウトプロパティを使用します。私のコードでは、最初にレイアウトが属性を要求されたとき、contentSize
はまだ設定されていませんでした。
CollectionViewおよびGoto Attribute Inspectorを選択します。 Prefetching EnabledCheckBoxのチェックを外します。これは私の問題を修正しました。 スクリーンショット