为什么对自定义 CALayer 属性进行动画处理会导致其他属性在动画过程中为零?

发布于 2024-09-29 08:15:09 字数 1772 浏览 4 评论 0原文

我有一个自定义 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动画结束时恢复为原始值。

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

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

我做了一个简单的项目来说明这个问题,您可以在这里找到: 说明问题的示例项目

可能有些东西我不明白 CoreAnimation 的工作原理,我'我进行了深入的搜索,但我没有任何线索。有人知道吗?

I have a custom CALayer (say CircleLayer), containing custom properties (radius and tint). The layer renders itself in its drawInContext: method.

- (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); 
}

I want the radius to be animatable so I've implemented

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

And the animation is performed like this:

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: gets called as expected during the animation to redraw the circle, however the tint is set to nil as soon as the animation starts and gets back to its original value when the animation ends.

I've concluded that if I want to animate a custom property and want other properties to keep their value during the animation, I have to animate them too, which I find not being convenient at all.

The purpose is not to grow/shrink a circle, I know I can use transformation for this. It is only to illustrate with a simple example the problem of animating a single custom property without having to animate all the other ones.

I've made a simple project illustrating the issue, which you can find here:
Sample project illustrating the issue

There is probably something I didn't get on how CoreAnimation works, I've performed intensive searching but I'm stuck with no clue. Anyone knows?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

被翻牌 2024-10-06 08:15:09

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

If I understood your question correctly, it goes like this. When you add an animation to a CALayer, it creates a so-called presentation copy of that layer using initWithLayer:. The presentation layer contains actual animated state for each animation frame, while the original layer has the final state. The problem with animating your own properties is that CALayer does not copy them all in initWithLayer:. If that's your case, your should override initWithLayer: and set up all the properties you need for animation, that is, both tint and radius.

深爱成瘾 2024-10-06 08:15:09
+ (BOOL)needsDisplayForKey:(NSString *)key {
    if ([key isEqualToString:@"radius"] || [key isEqualToString:@"tint"]) {
        return YES;
    }
    return [super needsDisplayForKey:key];
}

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

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

The animation may require all properties of the context to respond to a refresh.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文