CALayer 上的隐式动画没有动画

Implicit Animation on CALayer not animating

我正在制作一个我想要制作动画的圆形进度条。一切都按预期绘制并且有效,但是当我尝试制作动画时,我立即得到了变化而不是动画。任何帮助将不胜感激。

这是我的图层:

@interface CircularProgressLayer : CALayer

@property (nonatomic, assign) CGFloat progressDegrees;
@property (nonatomic, assign) CGFloat trackWidth;

@end

@implementation CircularProgressLayer : CALayer

@dynamic progressDegrees;
@dynamic trackWidth;

- (id)initWithLayer:(id)layer
{
    if ((self = [super initWithLayer:layer]))
    {
        if ([layer isKindOfClass:[CircularProgressLayer class]])
        {
            self.progressDegrees = ((CircularProgressLayer*)layer).progressDegrees;
        }
    }

    return self;
}

// Instruct to Core Animation that a change in the custom property should automatically trigger a redraw of the layer.
+ (BOOL)needsDisplayForKey:(NSString*)key
{
    if ([key isEqualToString:@"progressDegrees"])
    {
        return YES;
    }
    if ([key isEqualToString:@"trackWidth"])
    {
        return YES;
    }

    return [super needsDisplayForKey:key];
}

// Needed to support implicit animation of this property.
// Return the basic animation the implicit animation will leverage.
- (id<CAAction>)actionForKey:(NSString *)key
{
    if ([key isEqualToString:@"progressDegrees"])
    {
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key];
        animation.fromValue = [[self presentationLayer] valueForKey:key];
        animation.duration = 5.0;
        animation.fillMode = kCAFillModeForwards;
        animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];

        return animation;
    }
    return [super actionForKey:key];
}

据我了解,在 actionForKey: 中返回动画应该是使动画正常工作所需的全部内容,但我什么也没得到。我的所有绘图当前都包含在实现此层的视图的 drawLayer:inContext: 方法中(因为它起源于 drawRect 代码,然后我才知道我必须将它放在一个层中以使其具有动画效果,而且我还没有' t converted everything over), 但是 sample code 我从 Apple 下载的使它看起来很好。

下面是绘图代码:

-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
    UIGraphicsPushContext(context);
    [self drawTrackInContext:context];
    [self drawProgressInContext:context];
    UIGraphicsPopContext();
}

- (void)drawProgressInContext:(CGContextRef)context
{    
    if (self.progress < 1)
    {
        // No progress, get out
        return;
    }

    CGSize size = self.bounds.size;

    FlipContextVertically(size);
    CGContextSaveGState(context);
    {
        UIBezierPath *circle = [self circle];
        CGContextSetLineWidth(context, 0.0);

        CGFloat degrees = 360.0;

        for (int i = 0; i < degrees; i++)
        {
            if (i > ((RMONCircularProgressLayer *)self.layer).progressDegrees)
            {
                break;
            }

            CGFloat theta = 2 * M_PI * (CGFloat) i / degrees;
            CGFloat x = self.radius * sin(theta);
            CGFloat y = self.radius * cos(theta);

            MovePathCenterToPoint(circle, CGPointMake(x, y));
            OffsetPath(circle, CGSizeMake(size.width / 2, size.height / 2));

            CGContextSetFillColorWithColor(context, [self colorForDegree:i].CGColor);
            CGContextAddPath(context, circle.CGPath);
            CGContextDrawPath(context, kCGPathFillStroke);
        }
    }
    CGContextRestoreGState(context);
}

- (void)drawTrackInContext:(CGContextRef)context
{
    CGContextSaveGState(context);
    {
        // Draw the circle covering middle of the screen
        CGSize size = self.bounds.size;

        CGContextSetLineWidth(context, self.trackWidth);  // will only be filled color

        CGContextSetStrokeColorWithColor(context, self.trackColor.CGColor);
        CGContextSetFillColorWithColor(context, self.fillColor.CGColor);
        CGContextSetAlpha(context, 1.0);

        CGContextAddArc(context, (CGFloat)size.width/2.0, (CGFloat)size.height/2.0, self.radius, 0, (CGFloat)M_PI*2.0, 1);
        CGContextDrawPath(context, kCGPathFillStroke);
    }
    CGContextRestoreGState(context);
}

drawTrackInContext只是画了一个圆圈,里面会包含进度圈。 drawProgressInContext 是我希望制作动画的绘图代码。 iOS Erica Sadun 的绘图中有一些辅助函数,它们执行一些我没有包括的上下文和路径操作,函数名称应该是相当自我解释的。

如有任何帮助,我们将不胜感激。

仅供参考:如果更容易的话,我非常愿意将其制作成明确的动画,但我最终朝这个方向前进,因为我也无法做到这一点。

当图层 属性 被动画化时,属性 的值不会随时间变化以表示中间值。相反,层时间中给定时刻的 属性 的值是从活动动画派生的,并在层的 presentationLayer.

中可用。

当动画处于活动状态时,drawInContext: 在表示层而不是主层上调用。同样,-drawLayer:inContext: 接收表示层而不是主层。

您遇到的问题是 -drawProgressInContext: 不使用调用函数中的层,而是读取 self.layer.progressDegrees 以获取要绘制的值,该值将始终是分配的值.解决方法是将图层传递给该函数并读取值:

- (void)drawProgressInLayer:(CALayer *)layer context:(CGContextRef)context
{
    ...
    if (i > ((RMONCircularProgressLayer *)layer).progressDegrees) {
        ...

您在 if (self.progress < 1) 中止时也遇到了问题。这将使它在从非零值变为零时无法设置动画。同样的修复将适用:if ((RMONCircularProgressLayer *)layer).progressDegrees < 1)