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?
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];
}
Have the pie with the arrow as an image.
In every step, draw this image, rotating it a little. Draw the gray pie over the image, choosing correct angles for the image rotation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With