initialLayoutAttributesForAppearingItemAtIndexPath fired for all visible cells, not just inserted cells

前端 未结 4 1591
醉梦人生
醉梦人生 2021-01-31 11:03

Has anyone seen a decent answer to this problem?

initialLayoutAttributesForAppearingItemAtIndexPath seems to be being called for all visible cells, not just

相关标签:
4条回答
  • 2021-01-31 11:20

    You're not alone. The UICollectionViewLayout header file comments make things a little clearer.

    For each element on screen before the invalidation, finalLayoutAttributesForDisappearingXXX will be called and an animation setup from what is on screen to those final attributes.

    For each element on screen after the invalidation, initialLayoutAttributesForAppearingXXX will be called an an animation setup from those initial attributes to what ends up on screen.

    Basically finalLayoutAttributesForDisappearingItemAtIndexPath is called for each item on screen before the animation block starts, and initialLayoutAttributesForAppearingItemAtIndexPath is called for each item after the animation block ends. It's up to you to cache the array of UICollectionViewUpdateItem objects sent in prepareForCollectionViewUpdates so you know how to setup the initial and final attributes. In my case I cached the previous layout rectangles in prepareLayout so I knew the correct initial positions to use.

    One thing that stumped me for a while is you should use super's implementation of initialLayoutAttributesForAppearingItemAtIndexPath and modify the attributes it returns. I was just calling layoutAttributesForItemAtIndexPath in my implementation, and animations weren't working because the layout positions were different.

    0 讨论(0)
  • 2021-01-31 11:23

    I found this blog post by Mark Pospesel to be helpful.
    The author also fixed WWDC CircleLayout sample and posted it on Github.

    Methods of interest:

    - (void)prepareForCollectionViewUpdates:(NSArray *)updateItems
    {
        // Keep track of insert and delete index paths
        [super prepareForCollectionViewUpdates:updateItems];
    
        self.deleteIndexPaths = [NSMutableArray array];
        self.insertIndexPaths = [NSMutableArray array];
    
        for (UICollectionViewUpdateItem *update in updateItems)
        {
            if (update.updateAction == UICollectionUpdateActionDelete)
            {
                [self.deleteIndexPaths addObject:update.indexPathBeforeUpdate];
            }
            else if (update.updateAction == UICollectionUpdateActionInsert)
            {
                [self.insertIndexPaths addObject:update.indexPathAfterUpdate];
            }
        }
    }
    
    - (void)finalizeCollectionViewUpdates
    {
        [super finalizeCollectionViewUpdates];
        // release the insert and delete index paths
        self.deleteIndexPaths = nil;
        self.insertIndexPaths = nil;
    }
    
    // Note: name of method changed
    // Also this gets called for all visible cells (not just the inserted ones) and
    // even gets called when deleting cells!
    - (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
    {
        // Must call super
        UICollectionViewLayoutAttributes *attributes = [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath];
    
        if ([self.insertIndexPaths containsObject:itemIndexPath])
        {
            // only change attributes on inserted cells
            if (!attributes)
                attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
    
            // Configure attributes ...
            attributes.alpha = 0.0;
            attributes.center = CGPointMake(_center.x, _center.y);
        }
    
        return attributes;
    }
    
    // Note: name of method changed
    // Also this gets called for all visible cells (not just the deleted ones) and
    // even gets called when inserting cells!
    - (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
    {
        // So far, calling super hasn't been strictly necessary here, but leaving it in
        // for good measure
        UICollectionViewLayoutAttributes *attributes = [super finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath];
    
        if ([self.deleteIndexPaths containsObject:itemIndexPath])
        {
            // only change attributes on deleted cells
            if (!attributes)
                attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
    
            // Configure attributes ...
            attributes.alpha = 0.0;
            attributes.center = CGPointMake(_center.x, _center.y);
            attributes.transform3D = CATransform3DMakeScale(0.1, 0.1, 1.0);
        }
    
        return attributes;
    }
    
    0 讨论(0)
  • 2021-01-31 11:25

    Make sure you're using new method signature in Swift 3. Autocorrection doesn't work for this method:

    func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes?
    
    0 讨论(0)
  • 2021-01-31 11:36

    If you've subclassed UICollectionViewFlowLayout, you can call the super implementation. Once you've got the default initial layout, you can check for an .alpha of 0. If alpha is anything other than 0, the cell is being moved, if it's 0 it's being inserted.

    Bit of a hack, I know, but it works

    0 讨论(0)
提交回复
热议问题