动画CAShapeLayer Pie [英] Animated CAShapeLayer Pie

查看:120
本文介绍了动画CAShapeLayer Pie的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 CAShapeLayer 创建一个简单的动画饼图。我希望它从0动画到提供的百分比。

I am trying to create a simple and animated pie chart using CAShapeLayer. I want it to animate from 0 to a provided percentage.

要创建形状图层,我使用:

To create the shape layer I use:

CGMutablePathRef piePath = CGPathCreateMutable();
CGPathMoveToPoint(piePath, NULL, self.frame.size.width/2, self.frame.size.height/2);
CGPathAddLineToPoint(piePath, NULL, self.frame.size.width/2, 0);
CGPathAddArc(piePath, NULL, self.frame.size.width/2, self.frame.size.height/2, radius, DEGREES_TO_RADIANS(-90), DEGREES_TO_RADIANS(-90), 0);        
CGPathAddLineToPoint(piePath, NULL, self.frame.size.width/2 + radius * cos(DEGREES_TO_RADIANS(-90)), self.frame.size.height/2 + radius * sin(DEGREES_TO_RADIANS(-90)));

pie = [CAShapeLayer layer];
pie.fillColor = [UIColor redColor].CGColor;
pie.path = piePath;

[self.layer addSublayer:pie];

然后动画我使用:

CGMutablePathRef newPiePath = CGPathCreateMutable();
CGPathAddLineToPoint(newPiePath, NULL, self.frame.size.width/2, 0);    
CGPathMoveToPoint(newPiePath, NULL, self.frame.size.width/2, self.frame.size.height/2);
CGPathAddArc(newPiePath, NULL, self.frame.size.width/2, self.frame.size.height/2, radius, DEGREES_TO_RADIANS(-90), DEGREES_TO_RADIANS(125), 0);                
CGPathAddLineToPoint(newPiePath, NULL, self.frame.size.width/2 + radius * cos(DEGREES_TO_RADIANS(125)), self.frame.size.height/2 + radius * sin(DEGREES_TO_RADIANS(125)));    

CABasicAnimation *pieAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
pieAnimation.duration = 1.0;
pieAnimation.removedOnCompletion = NO;
pieAnimation.fillMode = kCAFillModeForwards;
pieAnimation.fromValue = pie.path;
pieAnimation.toValue = newPiePath;

[pie addAnimation:pieAnimation forKey:@"animatePath"];

显然,这是以非常奇怪的方式制作动画。形状只是成长为最终状态。有没有一种简单的方法可以让这个动画跟随圆圈的方向?或者是 CAShapeLayer 动画的限制?

Obviously, this is animating in a really odd way. The shape just kind of grows into its final state. Is there an easy way to make this animation follow the direction of the circle? Or is that a limitation of CAShapeLayer animations?

推荐答案

我知道这个问题早已得到解答,但我不认为这是 CAShapeLayer CAKeyframeAnimation 的好例子。 Core Animation有能力为我们做动画补间。这是一个类(包含 UIView ,如果你愿意的话),我用来很好地完成效果。

I know this question has long been answered, but I don't quite think this is a good case for CAShapeLayer and CAKeyframeAnimation. Core Animation has the power to do animation tweening for us. Here's a class (with a wrapping UIView, if you like) that I use to accomplish the effect pretty well.

图层子类为progress属性启用隐式动画,但视图类将其setter包装在 UIView 动画方法中。使用带有 UIViewAnimationOptionBeginFromCurrentState 的0.0长度动画的有趣(并且最终有用)副作用是每个动画取消前一个动画,从而产生平滑,快速,高帧率馅饼,如这个(动画)和这个(没有动画,但增加了。)

The layer subclass enables implicit animation for the progress property, but the view class wraps its setter in a UIView animation method. The interesting (and ultimately useful) side effect of using a 0.0 length animation with UIViewAnimationOptionBeginFromCurrentState is that each animation cancels out the previous one, leading to a smooth, fast, high-framerate pie, like this (animated) and this (not animated, but incremented).

DZRoundProgressView.h

@interface DZRoundProgressLayer : CALayer

@property (nonatomic) CGFloat progress;

@end

@interface DZRoundProgressView : UIView

@property (nonatomic) CGFloat progress;

@end

DZRoundProgressView.m

#import "DZRoundProgressView.h"
#import <QuartzCore/QuartzCore.h>

@implementation DZRoundProgressLayer

// Using Core Animation's generated properties allows
// it to do tweening for us.
@dynamic progress;

// This is the core of what does animation for us. It
// tells CoreAnimation that it needs to redisplay on
// each new value of progress, including tweened ones.
+ (BOOL)needsDisplayForKey:(NSString *)key {
    return [key isEqualToString:@"progress"] || [super needsDisplayForKey:key];
}

// This is the other crucial half to tweening.
// The animation we return is compatible with that
// used by UIView, but it also enables implicit
// filling-up-the-pie animations.
- (id)actionForKey:(NSString *) aKey {
    if ([aKey isEqualToString:@"progress"]) {
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:aKey];
        animation.fromValue = [self.presentationLayer valueForKey:aKey];
        return animation;
    }
    return [super actionForKey:aKey];
}

// This is the gold; the drawing of the pie itself.
// In this code, it draws in a "HUD"-y style, using
// the same color to fill as the border.
- (void)drawInContext:(CGContextRef)context {
    CGRect circleRect = CGRectInset(self.bounds, 1, 1);

    CGColorRef borderColor = [[UIColor whiteColor] CGColor];
    CGColorRef backgroundColor = [[UIColor colorWithWhite: 1.0 alpha: 0.15] CGColor];

    CGContextSetFillColorWithColor(context, backgroundColor);
    CGContextSetStrokeColorWithColor(context, borderColor);
    CGContextSetLineWidth(context, 2.0f);

    CGContextFillEllipseInRect(context, circleRect);
    CGContextStrokeEllipseInRect(context, circleRect);

    CGFloat radius = MIN(CGRectGetMidX(circleRect), CGRectGetMidY(circleRect));
    CGPoint center = CGPointMake(radius, CGRectGetMidY(circleRect));
    CGFloat startAngle = -M_PI / 2;
    CGFloat endAngle = self.progress * 2 * M_PI + startAngle;
    CGContextSetFillColorWithColor(context, borderColor);
    CGContextMoveToPoint(context, center.x, center.y);
    CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0);
    CGContextClosePath(context);
    CGContextFillPath(context);

    [super drawInContext:context];
}

@end

@implementation DZRoundProgressView
+ (Class)layerClass {
    return [DZRoundProgressLayer class];
}

- (id)init {
    return [self initWithFrame:CGRectMake(0.0f, 0.0f, 37.0f, 37.0f)];
}

- (id)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {
        self.opaque = NO;
        self.layer.contentsScale = [[UIScreen mainScreen] scale];
        [self.layer setNeedsDisplay];
    }
    return self;
}

- (void)setProgress:(CGFloat)progress {
    [(id)self.layer setProgress:progress];
}

- (CGFloat)progress {
    return [(id)self.layer progress];
}

@end

这篇关于动画CAShapeLayer Pie的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆