UICollectionViewLayout 部分插入与自我调整

问题描述

新年快乐! ?

我正在尝试使用动画插入和删除部分和元素来制作自定义布局。同时,重要的是所有元素都是自定大小

一切正常,除了一件事:获取其大小的请求,标题接收最后一个,尽管我需要它首先接收它。因此,我的单元格在动画开始时处于错误的位置。

我会尝试更详细地解释...

  1. 这一切都始于一个方法调用 prepare(forAnimatedBoundsChange:),在那里我更新了我的内部属性模型。
  2. initialLayoutAttributesForAppearingItem(at:) 方法被调用,其中对于我插入的部分中包含的每个单元格,在返回属性之前,我复制属性以便能够更改它们,因为我得到了它们的最终大小稍后。
override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    if itemIndexPathsToInsert.contains(itemIndexPath) || sectionIndicesToInsert.contains(itemIndexPath.section) {
      let item = sections.item(at: itemIndexPath).copy() as! InsetGroupedLayoutAttributes
      item.alpha = 0.0
      itemsForPendingAnimations[itemIndexPath] = item
      return item
    } else {
      return super.initialLayoutAttributesForAppearingItem(at: itemIndexPath)
    }
  }

3 然后调用 invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:) 方法获得最终尺寸

  override func invalidationContext(forPreferredLayoutAttributes preferredAttributes: UICollectionViewLayoutAttributes,withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutInvalidationContext {
    let invalidationContext = super.invalidationContext(forPreferredLayoutAttributes: preferredAttributes,withOriginalAttributes: originalAttributes)
    let indexPath = preferredAttributes.indexPath
    let height = preferredAttributes.frame.height.rounded(.up)
    
    switch preferredAttributes.representedElementCategory {
    case .cell:
      print("cell",#function)
      sections.updateItem(at: indexPath,withNewHeight: height)
      itemsForPendingAnimations[indexPath]?.frame = sections.item(at: indexPath).frame
     // itemsForPendingAnimations[indexPath]?.transform = .init(scaleX: 0.8,y: 0.8)
      invalidationContext.invalidateItems(at: [indexPath])
    case .supplementaryView:
      print("supplementary",#function)
      sections.updateHeader(forSectionIndex: indexPath.section,withNewHeight: height)
      supplementaryElementsForPendingAnimations[indexPath]?.frame = sections.supplementaryElement(at: indexPath).frame
      itemsForPendingAnimations.forEach { $0.value.frame = sections.item(at: $0.key).frame }
      invalidationContext.invalidateSupplementaryElements(ofKind: InsetGroupedHeaderCollectionReusableView.kind,at: [indexPath])
    default:
      assertionFailure()
    }
    
    return invalidationContext
  }
  1. 最后调用了layoutAttributesForItem(at:)方法,之后就不能再改变我复制的属性了,动画会从调用这个方法后属性保持的状态开始。

此外,将对插入部分的所有单元格重复从 2 到 4 的调用并且仅在此之后为补充元素调用相应的方法。 initialLayoutAttributesForAppearingSupplementaryElement(ofKind:at:)invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:)layoutAttributesForSupplementaryView(ofKind:at:)。在这里,我的标题获得了最终大小,此标题之后的所有单元格也应调整大小,但我无法再更改动画初始状态的属性。

记录调用以插入其中一个部分:

prepare(forCollectionViewUpdates:)
initialLayoutAttributesForAppearingItem(at:) [1,0]
cell invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:)
layoutAttributesForItem(at:) [1,0]
initialLayoutAttributesForAppearingItem(at:) [1,1]
cell invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:)
layoutAttributesForItem(at:) [1,1]
initialLayoutAttributesForAppearingItem(at:) [1,2]
cell invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:)
layoutAttributesForItem(at:) [1,2]
initialLayoutAttributesForAppearingItem(at:) [1,3]
cell invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:)
layoutAttributesForItem(at:) [1,3]
initialLayoutAttributesForAppearingItem(at:) [1,4]
cell invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:)
layoutAttributesForItem(at:) [1,4]
initialLayoutAttributesForAppearingSupplementaryElement(ofKind:at:) [1,0] <--- Should be called first
supplementary invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:) <--- Should be called first
layoutAttributesForSupplementaryView(ofKind:at:) [1,0] <--- Should be called first
finalizeCollectionViewUpdates()

因此,插入发生时单元格的轻微移动,因为在调整标题大小后我无法再更改它们的位置:

https://youtu.be/Lbj4ETO5HC0

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)