【问题标题】:Implicit Animation on CALayer not animatingCALayer 上的隐式动画没有动画
【发布时间】:2016-08-10 22:07:44
【问题描述】:

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

这是我的图层:

@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 代码,然后我才知道我必须把它放在一个层中才能让它动画,我还没有转换一切都结束了),但是我从 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 绘图中有一些辅助函数可以执行一些我没有包含的上下文和路径操作,函数名称应该是相当不言自明的。

任何帮助将不胜感激。

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

【问题讨论】:

  • 请在drawLayer:inContext:中显示您的代码
  • 添加了绘图代码,不知道为什么忘记了。

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


【解决方案1】:

当图层属性被动画化时,该属性的值不会随时间变化以表示中间值。相反,图层时间中给定时刻的属性值源自活动动画,并在图层的presentationLayer 中可用。

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

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

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

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

【讨论】:

  • 感谢您的详细解释。我遇到了使用表示层值来使动画工作的问题,但是我必须添加一个 if (isAnimating) 块来在 2 之间进行转换,而且我知道它必须更容易。我没有意识到我的绘图代码是用 2 个不同的层调用的。太棒了。
猜你喜欢
  • 1970-01-01
  • 2014-11-07
  • 2011-03-16
  • 1970-01-01
  • 2016-05-16
  • 2011-08-15
  • 1970-01-01
  • 2011-01-15
  • 1970-01-01
相关资源
最近更新 更多