initialLayoutAttributesForAppearingItemAtIndexPath 为所有可见单元格触发,而不仅仅是插入的单元格

Posted

技术标签:

【中文标题】initialLayoutAttributesForAppearingItemAtIndexPath 为所有可见单元格触发,而不仅仅是插入的单元格【英文标题】:initialLayoutAttributesForAppearingItemAtIndexPath fired for all visible cells, not just inserted cells 【发布时间】:2012-11-21 16:47:40 【问题描述】:

有没有人看到这个问题的正确答案?

initialLayoutAttributesForAppearingItemAtIndexPath 似乎被所有可见单元格调用,而不仅仅是被插入的单元格。根据Apple's own docs:

对于移动的项目,集合视图使用标准方法来检索项目的更新布局属性。对于插入或删除的项目,集合视图会调用一些不同的方法,您应该重写这些方法以提供适当的布局信息

这听起来不像正在发生的事情......其他单元格没有被插入,它们正在被移动,但它也为那些被移动的单元格调用initialLayoutAttributesForAppearingItemAtIndexPath

我已经看到使用prepareForCollectionViewUpdates: 来跟踪哪些 indexPaths 正在更新并且只更改那些的变通方法,但这似乎有点奇怪,因为它会再次出现在他们自己的文档中。有其他人找到解决此问题的更好方法吗?

【问题讨论】:

我也很难理解这一点。每次我对集合视图进行更改时,都会重新加载所有可见单元格,目前尚不清楚如何避免这种情况。当用户在一个部分中点击一个单元格时,我会重新加载另一部分,同时为点击的单元格中的更改设置动画。重新加载中断了我的动画,我找不到解决方法。 Collection View Programming Guide中也提到了。 【参考方案1】:

我发现 this blog post by Mark Pospesel 很有帮助。 作者还修复了WWDCCircleLayoutsample和posted it on Github。

感兴趣的方法:

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

【讨论】:

伙计,你拯救了我的一天!【参考方案2】:

你并不孤单。 UICollectionViewLayout 头文件 cmets 让事情变得更清晰一些。

对于失效前屏幕上的每个元素, finalLayoutAttributesForDisappearingXXX 将被调用并且 从屏幕上的内容到最终属性的动画设置。

对于失效后屏幕上的每个元素, initialLayoutAttributesForAppearingXXX 将被称为动画 从这些初始属性设置到屏幕上的最终属性。

基本上,finalLayoutAttributesForDisappearingItemAtIndexPath 在动画块开始之前为屏幕上的每个项目调用,initialLayoutAttributesForAppearingItemAtIndexPath 在动画块结束后为每个项目调用。您可以缓存prepareForCollectionViewUpdates 中发送的UICollectionViewUpdateItem 对象数组,以便您知道如何设置初始属性和最终属性。在我的例子中,我将之前的布局矩形缓存在 prepareLayout 中,所以我知道要使用的正确初始位置。

让我难过一阵子的一件事是你应该使用 super 的 initialLayoutAttributesForAppearingItemAtIndexPath 实现并修改它返回的属性。我只是在我的实现中调用layoutAttributesForItemAtIndexPath,但由于布局位置不同,动画无法正常工作。

【讨论】:

【参考方案3】:

如果您已将 UICollectionViewFlowLayout 子类化,则可以调用 super 实现。获得默认初始布局后,您可以检查 .alpha0。如果alpha 不是0,则表示正在移动单元格,如果是0,则表示正在插入单元格。

我知道,这有点小技巧,但它确实有效 ?。

Swift 2.0 实现如下:

override func initialLayoutAttributesForAppearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? 


    guard let attributes = super.initialLayoutAttributesForAppearingItemAtIndexPath(itemIndexPath) where attributes.alpha == 0 else 
        return nil
    

    // modify attributes for insertion here

    return attributes


【讨论】:

【参考方案4】:

确保您在 Swift 3 中使用新方法签名。自动更正不适用于此方法:

func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes?

【讨论】:

以上是关于initialLayoutAttributesForAppearingItemAtIndexPath 为所有可见单元格触发,而不仅仅是插入的单元格的主要内容,如果未能解决你的问题,请参考以下文章