【问题标题】:no UICollectionViewLayoutAttributes instance for -layoutAttributesForDecorationViewOfKind: shadowDecorationView at path-layoutAttributesForDecorationViewOfKind 没有 UICollectionViewLayoutAttributes 实例:路径上的 shadowDecorationView
【发布时间】:2021-11-30 03:52:56
【问题描述】:

我有一个带有自定义集合视图流布局的集合视图,该布局在某些部分后面添加了阴影。我收到了一位使用 iOS 13 的用户的崩溃报告,其中包含以下错误和堆栈跟踪:

Fatal Exception: NSInternalInconsistencyException
no UICollectionViewLayoutAttributes instance for -layoutAttributesForDecorationViewOfKind: shadowDecorationView at path <NSIndexPath: 0x96c5f0d4fd83d873> {length = 2, path = 4 - 0}

Fatal Exception: NSInternalInconsistencyException
0  CoreFoundation                 0x12a794 __exceptionPreprocess
1  libobjc.A.dylib                0x5bcc objc_exception_throw
2  CoreFoundation                 0x2d82c +[_CFXNotificationTokenRegistration keyCallbacks]
3  Foundation                     0x8716c -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:]
4  UIKitCore                      0x2a5d40 -[UICollectionViewData layoutAttributesForDecorationViewOfKind:atIndexPath:]
5  UIKitCore                      0x2a32c4 -[UICollectionViewData rectForDecorationElementOfKind:atIndexPath:]
6  UIKitCore                      0x283c50 -[UICollectionView _viewAnimationsForCurrentUpdate]
7  UIKitCore                      0x287d1c __71-[UICollectionView _updateWithItems:tentativelyForReordering:animator:]_block_invoke.1887
8  UIKitCore                      0xed51cc +[UIView(Animation) performWithoutAnimation:]
9  UIKitCore                      0x286f48 -[UICollectionView _updateWithItems:tentativelyForReordering:animator:]
10 UIKitCore                      0x2815b0 -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:]
11 UIKitCore                      0x288eb0 -[UICollectionView _endUpdatesWithInvalidationContext:tentativelyForReordering:animator:]
12 UIKitCore                      0x2891fc -[UICollectionView _performBatchUpdates:completion:invalidationContext:tentativelyForReordering:animator:]
13 UIKitCore                      0x289044 -[UICollectionView _performBatchUpdates:completion:invalidationContext:tentativelyForReordering:]
14 UIKitCore                      0x288fc8 -[UICollectionView _performBatchUpdates:completion:invalidationContext:]
15 UIKitCore                      0x288f04 -[UICollectionView performBatchUpdates:completion:]
16 IGListKit                      0x11c54 __67-[IGListAdapterUpdater performBatchUpdatesWithCollectionViewBlock:]_block_invoke.79 + 250 (IGListAdapterUpdater.m:250)
17 IGListKit                      0x111d8 -[IGListAdapterUpdater performBatchUpdatesWithCollectionViewBlock:] + 272 (IGListAdapterUpdater.m:272)
18 IGListKit                      0x12f78 __60-[IGListAdapterUpdater _queueUpdateWithCollectionViewBlock:]_block_invoke + 431 (IGListAdapterUpdater.m:431)
19 libdispatch.dylib              0x5a9a8 _dispatch_call_block_and_release
20 libdispatch.dylib              0x5b524 _dispatch_client_callout
21 libdispatch.dylib              0xd5b4 _dispatch_main_queue_callback_4CF$VARIANT$mp
22 CoreFoundation                 0xa87fc __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
23 CoreFoundation                 0xa36d0 __CFRunLoopRun
24 CoreFoundation                 0xa2ce8 CFRunLoopRunSpecific
25 GraphicsServices               0x338c GSEventRunModal
26 UIKitCore                      0xa2e444 UIApplicationMain
27 MyApp                           0x4b2c main + 28 (MyModeLogic.swift:28)
28 libdyld.dylib                  0x18f0 start

我的ShadowCollectionViewFlowLayout 看起来像这样:

protocol ShadowFlowLayoutDelegate: AnyObject {
  func shouldDisplayShadowFor(section: Int) -> Bool
}

final class ShadowCollectionViewFlowLayout: UICollectionViewFlowLayout {
    weak var decorationDelegate: ShadowFlowLayoutDelegate?
    private let decorationViewKind = "shadowDecorationView"

    override init() {
        super.init()
        register(ShadowReusableView.self, forDecorationViewOfKind: decorationViewKind)
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        register(ShadowReusableView.self, forDecorationViewOfKind: decorationViewKind)
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let parent = super.layoutAttributesForElements(in: rect)
        guard
            let attributes = parent,
            !attributes.isEmpty else {
          return parent
        }

        //attributes.forEach(configureRoundCornersAttributes)

        // Display shadows for every section by default
        var sectionsWithShadow = Set(attributes.map{$0.indexPath.section})

        if let decorationDelegate = decorationDelegate {
        // Ask decorationDelegate for sections with shadows, if it supports the protocol
          sectionsWithShadow = sectionsWithShadow.filter{decorationDelegate.shouldDisplayShadowFor(section: $0)}
        }

        let backgroundShadowAttributes: [UICollectionViewLayoutAttributes] = sectionsWithShadow.compactMap{ section in
            return layoutAttributesForDecorationView(
                ofKind: decorationViewKind,
                at: section
            )
        }

        return attributes + backgroundShadowAttributes
    }

    private func layoutAttributesForDecorationView(ofKind elementKind: String, at section: Int) -> UICollectionViewLayoutAttributes? {
        guard elementKind == decorationViewKind else {
            return nil
        }
        guard let numberOfItems = collectionView?.numberOfItems(inSection: section) else {
            return nil
        }

        let firstItemIndexPath = IndexPath(item: 0, section: section)
        guard let firstItemFrame = layoutAttributesForItem(at: firstItemIndexPath)?.frame else {
            return nil
        }
        let attributes = UICollectionViewLayoutAttributes(
            forDecorationViewOfKind: elementKind,
            with: firstItemIndexPath
        )

        let lastItemIndexPath = IndexPath(item: numberOfItems - 1, section: section)
        guard let lastItemFrame = layoutAttributesForItem(at: lastItemIndexPath)?.frame else {
            return nil
        }
        let heightDelta = lastItemFrame.maxY - firstItemFrame.minY

        let frame = CGRect(
            x: firstItemFrame.origin.x,
            y: firstItemFrame.origin.y,
            width: firstItemFrame.width,
            height: heightDelta
        )
        attributes.frame = frame
        attributes.zIndex = -1

        return attributes
    }
}

final class ShadowReusableView: UICollectionReusableView {
    override func layoutSubviews() {
        super.layoutSubviews()

        backgroundColor = .neutral(.oneHundred)
        layer.shadowOpacity = 0.3
        layer.shadowColor = UIColor.neutral(.nineHundred).cgColor
        layer.shadowRadius = 7
        layer.cornerRadius = 15
        layer.shadowOffset = CGSize(width: 0, height: 2)
        layer.masksToBounds = false
    }
}

我将我的布局添加到情节提要中或像这样以编程方式:

let collectionView = UICollectionView(
    frame: .zero,
    collectionViewLayout: ShadowCollectionViewFlowLayout()
)

...

let layout = collectionView.collectionViewLayout as? ShadowCollectionViewFlowLayout
layout?.decorationDelegate = self

究竟是什么导致了崩溃,我该如何解决?

【问题讨论】:

    标签: ios swift uicollectionview


    【解决方案1】:

    好的,我认为我已经解决了这个问题。问题是我没有覆盖layoutAttributesForDecorationView,我一直在使用具有相似名称的私有函数。所以我改变了它来覆盖它,现在它可以工作了。谁能告诉我为什么会这样?为什么它需要公开和覆盖?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-28
      • 2014-11-07
      • 2022-10-22
      • 1970-01-01
      • 2021-07-27
      相关资源
      最近更新 更多