为啥动画自定义 CALayer 属性会导致其他属性在动画期间为零?

Posted

技术标签:

【中文标题】为啥动画自定义 CALayer 属性会导致其他属性在动画期间为零?【英文标题】:Why animating custom CALayer properties causes other properties to be nil during animation?为什么动画自定义 CALayer 属性会导致其他属性在动画期间为零? 【发布时间】:2011-04-30 07:28:57 【问题描述】:

我有一个自定义 CALayer(比如 CircleLayer),包含自定义属性(半径和色调)。该层在其 drawInContext: 方法中呈现自身。

- (void)drawInContext:(CGContextRef)ctx 
    NSLog(@"Drawing layer, tint is %@, radius is %@", self.tint, self.radius);

    CGPoint centerPoint = CGPointMake(CGRectGetWidth(self.bounds)/2, CGRectGetHeight(self.bounds)/2);

    CGContextMoveToPoint(ctx, centerPoint.x, centerPoint.y);
    CGContextAddArc(ctx, centerPoint.x, centerPoint.y, [self.radius doubleValue], radians(0), radians(360), 0);
    CGContextClosePath(ctx);

    /* Filling it */
    CGContextSetFillColorWithColor(ctx, self.tint.CGColor);
    CGContextFillPath(ctx); 

我希望半径是可动画的,所以我已经实现了

+ (BOOL)needsDisplayForKey:(NSString *)key 
    if ([key isEqualToString:@"radius"]) 
        return YES;
    
    return [super needsDisplayForKey:key];

并且动画是这样执行的:

CABasicAnimation *theAnimation=[CABasicAnimation animationWithKeyPath:@"radius"];
theAnimation.duration=2.0;
theAnimation.fromValue=[NSNumber numberWithDouble:100.0];
theAnimation.toValue=[NSNumber numberWithDouble:50.0];
theAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

[circleLayer addAnimation:theAnimation forKey:@"animateRadius"];

circleLayer.radius = [NSNumber numberWithDouble:50.0];

drawInContext:在动画过程中按预期调用以重绘圆圈,但是动画开始时色调设置为 nil,动画结束时恢复为原始值。

我得出的结论是,如果我想为自定义属性设置动画并希望其他属性在动画期间保持其值,我也必须为它们设置动画,我觉得这根本不方便。

目的不是扩大/缩小一个圆圈,我知道我可以为此使用转换。这只是用一个简单的例子来说明动画单个自定义属性而不必为所有其他属性设置动画的问题。

我做了一个简单的项目来说明这个问题,你可以在这里找到: Sample project illustrating the issue

我可能没有了解 CoreAnimation 的工作原理,我进行了深入的搜索,但我一无所知。有谁知道吗?

【问题讨论】:

【参考方案1】:

如果我正确理解了您的问题,它是这样的。当您将动画添加到 CALayer 时,它会使用 initWithLayer: 创建该层的所谓演示副本。表示层包含每个动画帧的实际动画状态,而原始层具有最终状态。为自己的属性设置动画的问题是 CALayer 不会将它们全部复制到 initWithLayer: 中。如果是这种情况,您应该覆盖 initWithLayer: 并设置动画所需的所有属性,即色调和半径。

【讨论】:

这真的很有用。它为我节省了一天的时间。【参考方案2】:
+ (BOOL)needsDisplayForKey:(NSString *)key 
    if ([key isEqualToString:@"radius"] || [key isEqualToString:@"tint"]) 
        return YES;
    
    return [super needsDisplayForKey:key];

动画可能需要上下文的所有属性来响应刷新。

【讨论】:

以上是关于为啥动画自定义 CALayer 属性会导致其他属性在动画期间为零?的主要内容,如果未能解决你的问题,请参考以下文章

CALayer (CAAnimation) 动画属性绘制错误

删除 CALayer 内容属性的插值

SceneKit 中的自定义动画属性

CALayer 帧值即使在从超级层中移除后也会导致动画

通过 AVExportSession 导出带有隐式动画的 CALayer

在 Swift 中动画自定义 UIView 属性