你如何设置UICollectionViewanimation的持续时间?

我有一个自定义的stream布局,正在调整单元格的属性,当他们被插入和从以下两个函数从CollectionView中删除,但我无法弄清楚如何调整默认的animation持续时间。

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath { UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath]; // Assign the new layout attributes attributes.transform3D = CATransform3DMakeScale(0.5, 0.5, 0.5); attributes.alpha = 0; return attributes; 

}

 -(UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath{ UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath]; // Assign the new layout attributes attributes.transform3D = CATransform3DMakeScale(0.5, 0.5, 0.5); attributes.alpha = 0; return attributes; 

}

为了解决gavrix在答案中提出的问题,可以使用新属性CABasicAnimation *transformAnimation创buildUICollectionViewLayoutAttributes的子类,然后创build具有适当持续时间的自定义转换,并将其分配给initialLayoutAttributesForAppearingItemAtIndexPath中的属性,然后在UICollectionViewCell中根据需要应用这些属性:

 @interface AnimationCollectionViewLayoutAttributes : UICollectionViewLayoutAttributes @property (nonatomic, strong) CABasicAnimation *transformAnimation; @end @implementation AnimationCollectionViewLayoutAttributes - (id)copyWithZone:(NSZone *)zone { AnimationCollectionViewLayoutAttributes *attributes = [super copyWithZone:zone]; attributes.transformAnimation = _transformAnimation; return attributes; } - (BOOL)isEqual:(id)other { if (other == self) { return YES; } if (!other || ![[other class] isEqual:[self class]]) { return NO; } if ([(( AnimationCollectionViewLayoutAttributes *) other) transformAnimation] != [self transformAnimation]) { return NO; } return YES; } @end 

在Layout类中

 - (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath { AnimationCollectionViewLayoutAttributes* attributes = (AnimationCollectionViewLayoutAttributes* )[super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath]; CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform"]; transformAnimation.duration = 1.0f; CGFloat height = [self collectionViewContentSize].height; transformAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, 2*height, height)]; transformAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, attributes.bounds.origin.y, 0)]; transformAnimation.removedOnCompletion = NO; transformAnimation.fillMode = kCAFillModeForwards; attributes.transformAnimation = transformAnimation; return attributes; } 

然后在UICollectionViewCell中应用这些属性

 - (void) applyLayoutAttributes:(AnimationCollectionViewLayoutAttributes *)layoutAttributes { [[self layer] addAnimation:layoutAttributes.transformAnimation forKey:@"transform"]; } 

改变CALayer的速度

 @implementation Cell - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.layer.speed =0.2;//default speed is 1 } return self; } 

基于@ rotava的答案,你可以通过批量更新集合视图来临时设置animation速度:

 [self.collectionView performBatchUpdates:^{ [self.collectionView.viewForBaselineLayout.layer setSpeed:0.2]; [self.collectionView insertItemsAtIndexPaths: insertedIndexPaths]; } completion:^(BOOL finished) { [self.collectionView.viewForBaselineLayout.layer setSpeed:1]; }]; 

在布局过程的每个可能阶段尝试了[CATransaction setAnimationDuration:][UIView setAnimationDuration:]之后,我想出了一个有点不好的方法来改变由UICollectionView创build的细胞animation的持续时间,而不依赖于私有API。

您可以使用CALayerspeed属性来更改给定图层上执行的animation的相对媒体时间。 为了与UICollectionView一起工作,可以在单元图层layer.speed更改为小于1的值。 很显然,单元格的层总是具有非统一的animation速度并不是很好,所以有一种select是在准备单元格animation的时候发送一个NSNotification ,这个单元格animation会改变图层的速度,然后改变它的速度在animation结束后的适当的时候。

我不build议使用这种方法作为一个长期的解决scheme,因为它非常迂回,但它确实有效。 希望苹果未来能够为UICollectionViewanimation提供更多的select。

UICollectionView使用一些硬编码值在内部启动所有的animation。 但是,您始终可以重写该值直到提交animation。 一般来说,过程如下所示:

  • 开始animation
  • 获取所有的布局属性
  • 将属性应用于视图(UICollectionViewCell)
  • 提交animation

应用属性是在每个UICollectionViewCell下完成的,你可以用适当的方法重写animationDuration。 问题是,UICollectionViewCell有公共方法applyLayoutAttributes:但它的默认实现是空的! 基本上,UICollectionViewCell还有其他一些名为_setLayoutAttributes:私有方法_setLayoutAttributes:这个私有方法由UICollectionView调用,这个私有方法在最后调用applyLayoutAttributes:applyLayoutAttributes:之前,默认布局属性(如框架,位置,转换)将与当前animationDuration一起应用。 这就是说,你必须在私有方法_setLayoutAttributes:重载animationDuration _setLayoutAttributes:

 - (void) _setLayoutAttributes:(PSTCollectionViewLayoutAttributes *)layoutAttributes { [UIView setAnimationDuration:3.0]; [super _setLayoutAttributes:layoutAttributes]; } 

显然,这不是安全的。 您可以使用这些运行时攻击之一来安全地重写此私有方法。

你可以设置图层的速度属性(如[Rotoava's Answer])( https://stackoverflow.com/a/23146861/5271393 )来改变控制animation的速度。 问题是你使用的是任意值,因为你不知道插入animation的实际持续时间。

使用这个post你可以找出什么是默认的animation持续时间。

 newAnimationDuration = (1/layer.speed)*originalAnimationDuration layer.speed = originalAnimationDuration/newAnimationDuration 

如果你想制作长达400ms的animation,在你的布局中你可以:

 - (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewLayoutAttributes* attributes = [super finalLayoutAttributesForDisappearingItemAtIndexPath:indexPath]; //set attributes here UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath]; CGFloat originalAnimationDuration = [CATransaction animationDuration]; CGFloat newAnimationDuration = 0.4f; cell.layer.speed = originalAnimationDuration/newAnimationDuration; return attributes; } 

在我的情况下,我有可能被拖出屏幕的单元格,我想根据平移手势的速度更改删除animation的持续时间。

在手势识别器(这应该是你的集合视图的一部分):

 - (void)handlePanGesture:(UIPanGestureRecognizer *)sender { CGPoint dragVelocityVector = [sender velocityInView:self.collectionView]; CGFloat dragVelocity = sqrt(dragVelocityVector.x*dragVelocityVector.x + dragVelocityVector.y*dragVelocityVector.y); switch (sender.state) { ... case UIGestureRecognizerStateChanged:{ CustomLayoutClass *layout = (CustomLayoutClass *)self.collectionViewLayout; layout.dragSpeed = fabs(dragVelocity); ... } ... } 

然后在你的customLayout中:

 - (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewLayoutAttributes* attributes = [super finalLayoutAttributesForDisappearingItemAtIndexPath:indexPath]; CGFloat animationDistance = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); CGFloat originalAnimationDuration = [CATransaction animationDuration]; CGFloat newAnimationDuration = animationDistance/self.dragSpeed; UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath]; cell.layer.speed = originalAnimationDuration/newAnimationDuration; return attributes; } 

你可以改变UICollectionView layout.speed属性,这应该改变布局的animation持续时间…

自从forBaselineLayout以来,对@AshleyMills的更新已被弃用

这工作

 self.collectionView.performBatchUpdates({ () -> Void in let indexSet = IndexSet(0...(numberOfSections - 1)) self.collectionView.insertSections(indexSet) self.collectionView.forFirstBaselineLayout.layer.speed = 0.5 } , completion: { (finished) -> Void in self.collectionView.forFirstBaselineLayout.layer.speed = 1.0 })