【问题标题】:How to interpolate custom UICollectionViewLayoutAttributes properties with UICollectionViewTransitionLayout如何使用 UICollectionViewTransitionLayout 插入自定义 UICollectionViewLayoutAttributes 属性
【发布时间】:2014-03-18 12:45:53
【问题描述】:

我有两个使用自定义 UICollectionViewLayoutAttributes 子类的自定义 UICollectionViewLayout 对象。这些自定义属性添加了一个属性tintAlpha,该属性控制附加到每个集合视图单元格的色调叠加视图的不透明度。

我现在想使用UICollectionViewTransitionLayout 子类在这两种布局之间进行转换。如何配置过渡布局子类以在我的自定义布局属性上插入自定义 tintAlpha 属性?

我可以这样做:

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
     CustomLayoutAttributes *attr = [super layoutAttributesForItemAtIndexPath:indexPath];

     CustomLayoutAttributes *fromAttr = (CustomLayoutAttributes *)[self.currentLayout layoutAttributesForItemAtIndexPath:indexPath];
     CustomLayoutAttributes *toAttr = (CustomLayoutAttributes *)[self.nextLayout layoutAttributesForItemAtIndexPath:indexPath];

     CGFloat t = self.transitionProgress;
     attr.tintAlpha = (1.0f - t) * fromAttr.tintAlpha + t * toAttr.tintAlpha;

     return attr;
}

但是,这将忽略应用于当前或下一个布局中initialLayoutAttributesForAppearingItemAtIndexPath:finalLayoutAttributesForDisappearingItemAtIndexPath: 属性的任何更改,因此实际上并不正确。据我所知,UICollectionViewTransitionLayout 的默认实现确定了适当的 from/to 属性并将它们缓存在prepareLayoutlayoutAttributesForItemAtIndexPath: 中。在UICollectionViewTransitionLayout 上拥有一些公共 API 以允许我们从属性对象访问这些属性对象会非常有用,就好像我尝试实现我自己的逻辑来确定是否使用初始/最终属性与标准属性必然与默认实现有一些差异。

有没有更好的方法在布局转换期间插入这些自定义属性?


更新:

我刚刚遇到了这个场景的另一个问题。在上面的代码中,当直接从当前/下一个布局中获取fromAttrtoAttr 时,collectionView 是当前布局的nil(至少在转换的第一个运行循环之外)。如果布局完全取决于集合视图的边界——例如考虑一个简单的封面流布局——那么fromAttr 将不正确。

我真的很想在UICollectionViewTransitionLayout 上找到一个可以被子类覆盖的interpolatedLayoutAttributesFromLayoutAttributes:toLayoutAttributes:progress:

【问题讨论】:

  • github.com/wtmoose/TLLayoutTransitioning 是我用来做这个的。希望蒂莫西·穆斯(Timothy Moose)能来,并以一个很好的解释来索取他的赏金。否则,我会想出一个答案
  • 感谢@jrturton - 在遇到UICollectionView(例如stackoverflow.com/q/21422584/429427)的其他问题之前,我实际上已经看过这个项目,但我决定编写自己的子类。如果我错了,请纠正我,但 Timothy 的代码存在上述相同的问题 - 也就是说,没有考虑初始/最终布局属性,而他的 prepareLayout 本质上是对插值 UICollectionViewTransitionLayout 的重写。它还相当低效地在每次失效时为每个项目准备属性(在我的情况下不适合)。
  • 我现在已经做了类似的解决方法(根据[super layoutAttributesForElementsInRect:] 返回的属性缓存 from/to 属性),这允许我插入自定义属性。但是我试图复制决定是否使用初始/最终属性的逻辑,这让我很困扰(更不用说插值两次的开销了)!我已经为 API 提交了一个增强请求,以允许覆盖一个方法以启用插入自定义属性属性。在那之前,我一直希望有更好的方法?

标签: ios transitions uicollectionviewlayout


【解决方案1】:

在提出更好的解决方案之前,我已经实施了以下解决方法...

默认实现从[super prepareLayout] 调用当前和下一个布局,以选择和缓存需要从/到转换的布局属性。因为我们无法访问这个缓存(我的主要抱怨!),我们不能在过渡期间直接使用它们。相反,当默认实现调用内插布局属性时,我会构建自己的这些属性缓存。这只能在layoutAttributesForElementsInRect: 中发生(接近currentLayout.collectionView == nil 的问题),但幸运的是,这个方法似乎首先在与转换开始相同的运行循环中调用,并且在collectionView 属性设置为@之前987654325@。这提供了一个机会来建立我们的 from/to 布局属性并在过渡期间缓存它们。

@interface CustomTransitionLayout ()
@property(nonatomic, strong) NSMutableDictionary *transitionInformation;
@end

@implementation

- (void)prepareLayout
{
    [super prepareLayout];

    if (!self.transitionInformation) {
        self.transitionInformation = [NSMutableDictionary dictionary];
    }
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    // Let the super implementation tell us which attributes are required.
    NSArray *defaultLayoutAttributes = [super layoutAttributesForElementsInRect:rect];
    NSMutableArray *layoutAttributes = [NSMutableArray arrayWithCapacity:[defaultLayoutAttributes count]];
    for (UICollectionViewLayoutAttributes *defaultAttr in defaultLayoutAttributes) {
        UICollectionViewLayoutAttributes *attr = defaultAttr;
        switch (defaultAttr.representedElementCategory) {
            case UICollectionElementCategoryCell:
                attr = [self layoutAttributesForItemAtIndexPath:defaultAttr.indexPath];
                break;
            case UICollectionElementCategorySupplementaryView:
                attr = [self layoutAttributesForSupplementaryViewOfKind:defaultAttr.representedElementKind atIndexPath:defaultAttr.indexPath];
                break;
            case UICollectionElementCategoryDecorationView:
                attr = [self layoutAttributesForDecorationViewOfKind:defaultAttr.representedElementKind atIndexPath:defaultAttr.indexPath];
                break;
        }
        [layoutAttributes addObject:attr];
    }
    return layoutAttributes;
}


layoutAttributesForElementsInRect: 的覆盖只是为super 想要为其返回属性的每个元素索引路径调用layoutAttributesFor...atIndexPath:,它会缓存从/到属性。例如,layoutAttributesForItemAtIndexPath: 方法看起来像这样:

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSIndexPath *indexPathKey = [indexPath collectionViewKey];

    NSMutableDictionary *info = self.transitionInformation[indexPathKey];
    if (!info) {
        info = [NSMutableDictionary dictionary];
        self.transitionInformation[indexPathKey] = info;
    }

    // Logic to choose layout attributes to interpolate from.
    // (This is not exactly how the default implementation works, but a rough approximation)
    MyLayoutAttributes *fromAttributes = info[TransitionInfoFromAttributesKey];
    if (!fromAttributes) {
        MyLayoutAttributes *standardToAttributes = (MyLayoutAttributes *)[self.nextLayout layoutAttributesForItemAtIndexPath:indexPathKey];
        MyLayoutAttributes *initialAttributes = (MyLayoutAttributes *)[self.nextLayout initialLayoutAttributesForAppearingItemAtIndexPath:indexPathkey];
        if (initialAttributes && ![initialAttributes isEqual:standardToAttributes]) {
            fromAttributes = [initialAttributes copy];
        } else {
            fromAttributes = [(MyLayoutAttributes *)[self.currentLayout layoutAttributesForItemAtIndexPath:indexPathKey] copy];
        }
        info[TransitionInfoFromAttributesKey] = fromAttributes;
    }

    MyLayoutAttributes *toAttributes = info[TransitionInfoToAttributesKey];
    if (!toAttributes) {
        // ... similar logic as for fromAttributes ...
        info[TransitionInfoToAttributesKey] = toAttributes;
    }

    MyLayoutAttributes *attributes = [self interpolatedLayoutAttributesFromLayoutAttributes:fromAttributes
                                                                         toLayoutAttributes:toAttributes
                                                                                   progress:self.transitionProgress];
    return attributes;
}


这只是留下了一种新方法来进行实际插值,您不仅要插值自定义布局属性属性,而且重新实现默认插值(center/size/@987654334 @/transform/transform3D):

- (MyLayoutAttributes *)interpolatedLayoutAttributesFromLayoutAttributes:(MyLayoutAttributes *)fromAttributes
                                                      toLayoutAttributes:(MyLayoutAttributes *)toAttributes
                                                                progress:(CGFloat)progress
{
    MyLayoutAttributes *attributes = [fromAttributes copy];

    CGFloat t = progress;
    CGFloat f = 1.0f - t;

    // Interpolate all the default layout attributes properties.
    attributes.center = CGPointMake(f * fromAttributes.x + t * toAttributes.center.x,
                                    f * fromAttributes.y + t * toAttributes.center.y);
    // ...

    // Interpolate any custom layout attributes properties.
    attributes.customProperty = f * fromAttributes.customProperty + t * toAttributes.customProperty;
    // ...

    return attributes;
}


总结...

因此,令人沮丧的是,它包含大量代码(为简洁起见,此处未显示很多代码),其中大部分只是复制或尝试复制默认实现的内容反正做。如果UICollectionViewTransitionLayout 公开了一个要覆盖的方法,这会导致性能下降,并浪费开发时间来如此简单得多,例如:

- (UICollectionViewLayoutAttributes *)interpolatedLayoutAttributesFromLayoutAttributes:(UICollectionViewLayoutAttributes *)fromAttributes
                                                                    toLayoutAttributes:(UICollectionViewLayoutAttributes *)toAttributes
                                                                              progress:(CGFloat)progress
{
    MyLayoutAttributes *attributes = (MyLayoutAttributes *)[super interpolatedLayoutAttributesFromLayoutAttributes:fromAttributes toLayoutAttributes:toAttributes progress:progress];
    attributes.customProperty = (1.0f - progress) * fromAttributes.customProperty + progress * toAttributes.customProperty;
    return attributes;
}


这种解决方法的好处是您不必重新实现决定哪些布局属性在转换的开始/结束时可见的代码 - 默认实现为我们完成了这项工作。我们也不必在每次布局失效时获取 everything 的属性,然后检查与可见矩形相交的项目。

【讨论】:

  • 我真的很喜欢这个主意,但恐怕你给我们的时间不够多。有什么方法可以在某处发布示例项目吗?另外,[indexPath collectionViewKey] 是什么意思??
猜你喜欢
  • 2019-09-19
  • 1970-01-01
  • 1970-01-01
  • 2012-05-13
  • 1970-01-01
  • 1970-01-01
  • 2018-12-05
  • 2014-11-07
  • 1970-01-01
相关资源
最近更新 更多