Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
267 views
in Technique[技术] by (71.8m points)

ios - UICollectionViewLayout section insertion with self-sizing

Happy New Year! ??

I'm trying to make a custom layout with animated inserts and deletions of sections and elements. At the same time, it is important that all the elements are self-sized.

Everything works as it should, except for one thing: The request to get its size, the header receives the very last, although I need it to receive it the very first. Because of this, my cells are in the wrong position at the start of the animation.

I'll try to explain in more detail ...

  1. It all starts with a method call prepare(forAnimatedBoundsChange:), where I update my internal attributes model.
  2. initialLayoutAttributesForAppearingItem(at:) method is called, where for each cell that is contained in the section that I insert, and before returning attributes, I copy the attributes in order to be able to change them, since I get their final size later.
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 Then invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:) method is called where I get the final size

  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. Finally, layoutAttributesForItem(at:) method is called, after which, I can no longer change the attributes that I copied, the animation will start from the state in which the attributes remained after calling this method.

Further, calls from 2 to 4 will be repeated for all cells of the inserted sections AND ONLY AFTER the corresponding methods are called for supplementary elements. initialLayoutAttributesForAppearingSupplementaryElement(ofKind:at:), invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:) and layoutAttributesForSupplementaryView(ofKind:at:). Here my headers gets its final size, and all the cells after this title should also be resized, but I can no longer change the attributes for the initial state of the animations.

Logging calls to insert one of the sections:

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()

As a result, the insertion occurs with a slight movement of the cells, since I can no longer change their position after resizing the header:

https://youtu.be/Lbj4ETO5HC0

question from:https://stackoverflow.com/questions/65540708/uicollectionviewlayout-section-insertion-with-self-sizing

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...