我现在想使用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属性,并将它们缓存在prepareLayout或layoutAttributesForItemAtIndexPath:中.在UICollectionViewTransitionLayout上使用一些公共API可以让我们从/到属性对象访问这个方法是非常有用的,就好像我尝试实现我自己的逻辑关于是使用初始/最终属性还是绑定的标准属性与默认实现有一些差异.
在布局转换期间是否有更好的方法来插入这些自定义属性?
更新:
我刚刚遇到了一个额外的问题,这种情况.在上面的代码中,当从Attt& toAttr直接从当前/下一个布局,collectionView对于当前布局为零(至少在第一个运行循环之外).如果布局完全取决于集合视图的边界 – 例如,可以考虑一个简单的封面流布局,那么,从安装程序将不正确.
我真的在为一个interpolatedLayoutAttributesFromLayoutAttributes:toLayoutAttributes:progress:在UICollectionViewTransitionLayout上可以被子类覆盖.
解决方法
默认实现调用到当前&下一个布局从[super prepareLayout]选择&缓存需要从/转换到的布局属性.因为我们没有访问这个缓存(我的主要抱怨!),我们不能直接在转换期间使用它.相反,当默认实现通过插值的布局属性调用时,我构造自己的这些属性缓存.这只能在layoutAttributesForElementsInRect中发生:(靠近currentLayout.collectionView == nil的问题),但幸运的是,似乎这个方法首先在与转换开始相同的运行循环中调用,并且在将CollectionView属性设置为nil之前.这样就可以建立我们的从/到布局属性,并在转换期间缓存它们.
@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的替代:简单地调用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 / 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暴露了一个单独的方法来覆盖,那么这样做会导致更差的性能,并浪费开发时间,如果真的要简单得多,比如:
- (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;
}
这个解决方法的好处是,您不必重新实现代码,从而决定在转换的开始/结束时可以看到哪些布局属性 – 默认实现对我们来说是这样.每当布局无效时,我们也不必得到所有的属性,然后检查与可见直线相交的项目.