我有一个自定义流布局,它在使用以下两个函数从CollectionView插入和删除单元格时调整单元格的属性,但我无法弄清楚如何调整默认动画持续时间。 / p>
- (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;
}
答案 0 :(得分:27)
解决answer by gavrix中提出的没有黑客的问题
您可以使用新属性CABasicAnimation *transformAnimation
继承UICollectionViewLayoutAttributes,而不是创建具有合适持续时间的自定义转换并将其分配给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
在布局类
中- (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;
}
+ (Class)layoutAttributesClass {
return [AnimationCollectionViewLayoutAttributes class];
}
然后在UICollectionViewCell中应用属性
- (void) applyLayoutAttributes:(AnimationCollectionViewLayoutAttributes *)layoutAttributes
{
[[self layer] addAnimation:layoutAttributes.transformAnimation forKey:@"transform"];
}
答案 1 :(得分:18)
更改CALayer的速度
@implementation Cell
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.layer.speed =0.2;//default speed is 1
}
return self;
}
答案 2 :(得分:10)
在@ rotava的答案基础上,您可以使用集合视图的批量更新临时设置动画速度:
[self.collectionView performBatchUpdates:^{
[self.collectionView.viewForBaselineLayout.layer setSpeed:0.2];
[self.collectionView insertItemsAtIndexPaths: insertedIndexPaths];
} completion:^(BOOL finished) {
[self.collectionView.viewForBaselineLayout.layer setSpeed:1];
}];
答案 3 :(得分:4)
UICollectionView
使用一些硬编码值在内部启动所有动画。但是,在提交动画之前,您始终可以覆盖该值。
通常,流程如下所示:
应用属性在每个UICollectionViewCell下完成,您可以用适当的方法覆盖animationDuration。问题是UICollectionViewCell有公共方法applyLayoutAttributes:
但它的默认实现是空的!基本上,UICollectionViewCell有另一个名为_setLayoutAttributes:
的私有方法,这个私有方法由UICollectionView调用,这个私有方法最后调用applyLayoutAttributes:
。在调用animationDuration
之前,默认布局属性(如帧,位置,变换)将应用当前applyLayoutAttributes:
。
也就是说,您必须在私有方法animationDuration
_setLayoutAttributes:
- (void) _setLayoutAttributes:(PSTCollectionViewLayoutAttributes *)layoutAttributes
{
[UIView setAnimationDuration:3.0];
[super _setLayoutAttributes:layoutAttributes];
}
这显然不是applestore-safe。您可以使用其中一个运行时黑客安全地覆盖此私有方法。
答案 4 :(得分:4)
在布局过程的每个可能阶段尝试[CATransaction setAnimationDuration:]
和[UIView setAnimationDuration:]
后都没有成功,我想出了一种有点hacky方式来改变由UICollectionView
创建的单元格动画的持续时间不依赖私有API。
您可以使用CALayer
的{{1}}属性更改在给定图层上执行的动画的相对媒体时间。要使其与speed
一起使用,您可以在单元格图层上将UICollectionView
更改为小于1的值。显然,单元格的层总是具有非统一的动画速度并不是很好,因此一个选项是在准备单元格动画时调度layer.speed
,单元格订阅,这将改变图层速度,以及然后在动画结束后的适当时间将其更改回来。
我不建议使用这种方法作为长期解决方案,因为它非常迂回,但确实有效。希望Apple将来会为UICollectionView动画提供更多选项。
答案 5 :(得分:3)
您可以设置图层的速度属性(例如在Rotoava's Answer中)以更改控件的动画速度。问题是您使用的是任意值,因为您不知道插入动画的实际持续时间。
使用this post可以确定默认动画持续时间是什么。
newAnimationDuration = (1/layer.speed)*originalAnimationDuration
layer.speed = originalAnimationDuration/newAnimationDuration
如果你想让动画长达400ms,你可以在布局中使用:
- (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;
}
在我的情况下,我有可以从屏幕拖出的单元格,我想根据平移手势的速度更改删除动画的持续时间。
在手势识别器中(应该是集合视图的一部分):
- (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;
}
答案 6 :(得分:1)
@AshleyMills更新,因为不推荐使用forBaselineLayout
这有效
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
})
答案 7 :(得分:1)
没有子类化:
[UIView animateWithDuration:2.0 animations:^{
[self.collection reloadSections:indexSet];
}];
答案 8 :(得分:0)
您可以更改UICollectionView layout.speed属性,该属性应更改布局的动画持续时间...