如何使用UICollectionViewTransitionLayout插入自定义的UICollectionViewLayoutAttributes属性

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

我现在想使用UICollectionViewTransitionLayout子类在这两个布局之间转换。 如何configuration转换布局子类以在自定义布局属性上插入自定义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属性并将它们caching在prepareLayoutlayoutAttributesForItemAtIndexPath: 。 在UICollectionViewTransitionLayout上使用一些公共的API将是非常有用的,这样我们就可以从属性对象访问这些对象,就好像我尝试和实现自己的逻辑关于是否使用初始/最终属性vs标准属性与默认实现有一些差异。

有没有更好的方法来插入布局过渡期间这些自定义属性?


更新:

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

我真的想要一个interpolatedLayoutAttributesFromLayoutAttributes:toLayoutAttributes:progress:UICollectionViewTransitionLayout可以由子类覆盖。

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

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

 @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:的覆盖layoutAttributesForElementsInRect:只需调用layoutAttributesFor...atIndexPath:对于super想要返回属性的每个元素索引path,它会cachingfrom / to属性。 例如, 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 / alpha / 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暴露了一个重载的方法,这会导致性能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; } 

这个解决方法的好处在于,您不必重新实现决定哪些布局属性在转换的开始/结束时可见的代码 – 默认实现对我们来说是可行的。 每次布局无效时,我们也不必获取所有内容的属性,然后检查与可见矩形相交的项目。