Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
388 views
in Technique[技术] by (71.8m points)

iphone - Animated CAShapeLayer Pie

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];

Then to animate I use:

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"];

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...