Animated pie chart in iOS

animation, calayer, ios, objective-c, pie-chart

Solution

Okay, I've found the solution.

Building on the work Zachary Waldowski created (see my original post), I was able to do the entire thing in a `CALayer`.

In a nutshell, I draw one outer circle in maroon, a smaller circle in light gray, a stroked path in white, then draw the triangle for the tip of the arrow by hand.

Here's the relevant section of code that does the magic:

- (void)drawInContext:(CGContextRef)context {
    CGRect circleRect = CGRectInset(self.bounds, 1, 1);
    CGFloat startAngle = -M_PI / 2;
    CGFloat endAngle = self.progress * 2 * M_PI + startAngle;

    CGColorRef outerPieColor = [[UIColor colorWithRed: 137.0 / 255.0 green: 12.0 / 255.0 blue: 88.0 / 255.0 alpha: 1.0] CGColor];
    CGColorRef innerPieColor = [[UIColor colorWithRed: 235.0 / 255.0 green: 214.0 / 255.0 blue: 227.0 / 255.0 alpha: 1.0] CGColor];
    CGColorRef arrowColor = [[UIColor whiteColor] CGColor];

    // Draw outer pie
    CGFloat outerRadius = CGRectGetMidX(circleRect);
    CGPoint center = CGPointMake(CGRectGetMidX(circleRect), CGRectGetMidY(circleRect));

    CGContextSetFillColorWithColor(context, outerPieColor);
    CGContextMoveToPoint(context, center.x, center.y);
    CGContextAddArc(context, center.x, center.y, outerRadius, startAngle, endAngle, 0);
    CGContextClosePath(context);
    CGContextFillPath(context);

    // Draw inner pie
    CGFloat innerRadius = CGRectGetMidX(circleRect) * 0.45;

    CGContextSetFillColorWithColor(context, innerPieColor);
    CGContextMoveToPoint(context, center.x, center.y);
    CGContextAddArc(context, center.x, center.y, innerRadius, startAngle, endAngle, 0);
    CGContextClosePath(context);
    CGContextFillPath(context);

    // Draw the White Line
    CGFloat lineRadius = CGRectGetMidX(circleRect) * 0.72;
    CGFloat arrowWidth = 0.35;

    CGContextSetStrokeColorWithColor(context, arrowColor);
    CGContextSetFillColorWithColor(context, arrowColor);

    CGMutablePathRef path = CGPathCreateMutable();
    CGContextSetLineWidth(context, 16);
    CGFloat lineEndAngle = ((endAngle - startAngle) >= arrowWidth) ? endAngle - arrowWidth : endAngle;
    CGPathAddArc(path, NULL, center.x, center.y, lineRadius, startAngle, lineEndAngle, 0);
    CGContextAddPath(context, path);
    CGContextStrokePath(context);

    // Draw the Triangle pointer
    CGFloat arrowStartAngle = lineEndAngle - 0.01;
    CGFloat arrowOuterRadius = CGRectGetMidX(circleRect) * 0.90;
    CGFloat arrowInnerRadius = CGRectGetMidX(circleRect) * 0.54;

    CGFloat arrowX = center.x + (arrowOuterRadius * cosf(arrowStartAngle));
    CGFloat arrowY = center.y + (arrowOuterRadius * sinf(arrowStartAngle));

    CGContextMoveToPoint   (context, arrowX, arrowY);  // top corner

    arrowX = center.x + (arrowInnerRadius * cosf(arrowStartAngle));
    arrowY = center.y + (arrowInnerRadius * sinf(arrowStartAngle));

    CGContextAddLineToPoint(context, arrowX, arrowY);  // bottom corner

    arrowX = center.x + (lineRadius * cosf(endAngle));
    arrowY = center.y + (lineRadius * sinf(endAngle));

    CGContextAddLineToPoint(context, arrowX, arrowY);  // point
    CGContextClosePath(context);
    CGContextFillPath(context);

    [super drawInContext: context];
}

Problem

I'm trying to create an animated pie chart in iOS that acts basically like this: In a nutshell, it starts as a grey circle, and as the animation progresses the arrow moves around the circle until it gets to the percentage I specify. I've used the sample code that Zachary Waldowski posted in this SO question: Animated CAShapeLayer Pie That's gotten me to the point where I can create the basic animation. The maroon pie piece grows to hit the correct size. What I'm struggling with is how to map the arrow to the animation so that it gets dragged along as the pie piece grows. Any thoughts on how I can accomplish this?

Original source

Related problems