web-dev-qa-db-ja.com

iOS7のUICollectionViewLayoutAttributesのUICollectionView例外

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;レイアウトを無効にすることなく」

27
Jirune

更新する前に既存のレイアウトを無効にする必要があります。エラーメッセージの最後を参照してください。

レイアウトを無効にすることなく」

[collectionViewLayout invalidateLayout];

ICollectionViewLayoutのAppleドキュメント

18
Tim

iOS 10

IOS 10で新機能が導入されました。これはCell PrefetchingSupplementaryViewの動的な位置がクラッシュします。古い動作で実行するには、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 に関するリファレンス。


iOS 9以前...

@ Away Linが正しい。 。そのデリゲートメソッドを実装することで同じ問題を解決します。

ぼくの Custom UICollectionViewLayoutlayoutAttributesForElementsInRectの属性を変更します。セクションの位置は静的ではなく動的です。ですから、layout attributes for supplementary item at index path ... changed from ... to ...。変更する前に、invalideLayout関連メソッドを呼び出す必要があります。

そして、このデリゲートメソッドを実装してtrueを返すと、メソッドinvalidateLayoutWithContext:は、UICollectionViewLayoutをスクロールするときに呼び出されます。デフォルトでは、falseを返します。

- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
}

Apple Docs から

戻り値コレクションビューでレイアウトの更新が必要な場合はtrue、レイアウトを変更する必要がない場合はfalse。

説明このメソッドのデフォルトの実装はfalseを返します。サブクラスはそれをオーバーライドし、コレクションビューの境界の変更でセルおよび補足ビューのレイアウトの変更が必要かどうかに基づいて、適切な値を返すことができます。

コレクションビューの境界が変更され、このメソッドがtrueを返す場合、コレクションビューはinvalidateLayoutWithContext:メソッドを呼び出してレイアウトを無効にします。

IOS 6.0以降で利用できます。


もっと ...

カスタムUICollectionViewLayout用のGitHubの素敵なサンプルプロジェクト

62
AechoLiu

同じ例外がありました。iOS7では、Appleのドキュメント here に記載されているように、UICollectionViewLayoutAttributesサブクラスの継承されたisEqual:をオーバーライドする必要があります。

11
Spi

UICollectionViewFlowLayoutのサブクラスでメソッドをオーバーライドすることで問題を解決しました:

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBound

yESを返す

4
Away Lin

方法や理由は完全にはわかりませんが、これは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:この時点では、キャッシュが実際には何の目的も果たしていないことを認識しています:)

2
brandonscript

コレクションビューのコンテンツサイズに依存するコードがあったため、この問題も発生しました。私のコードは、collectionViewContentSizeではなくcollectionView!.contentSizeを介してコンテンツサイズにアクセスしていました。

前者はcollectionViewUICollectionViewLayoutプロパティを使用し、後者はカスタム実装されたレイアウトプロパティを使用します。私のコードでは、最初にレイアウトが属性を要求されたとき、contentSizeはまだ設定されていませんでした。

1
JSquared

CollectionViewおよびGoto Attribute Inspectorを選択します。 Prefetching EnabledCheckBoxのチェックを外します。これは私の問題を修正しました。 スクリーンショット