CALayer上的隐式动画没有动画效果

时间:2016-04-19 00:10:33

标签: ios objective-c animation core-animation calayer

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

这是我的图层:

@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转换了所有东西),但我从Apple下载的sample code使它看起来很好。

这是绘图代码:

-(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是我期望动画的绘图代码。 Erica Sadun的iOS绘图中有一些辅助函数做了一些我没有包含的上下文和路径操作,函数名称应该是相当自我解释的。

非常感谢任何帮助。

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

1 个答案:

答案 0 :(得分:2)

对图层属性进行动画处理时,该属性的值不会随时间变化以表示中间值。相反,图层时间中给定时刻的属性值是从活动动画中导出的,并在图层的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)