Question

I am drawing Donut Chart.I am using CGPath to draw and after drawing adding it to CAShapeLayer. First I draw an outer arc and then draw line toward centre, now I want to add arc to my current point.

Screenshot of this is below.

enter image description here

See smaller arc should be at the end of line.

My code,

for (int j = 0; j < numSubject; j++) {
            int valueForSubject = [datasource valueOfSubjectAtIndex:j andLevel:i inDonutChartView:self];
            endAngle =  [self angleInRadianFromSubjectValue:valueForSubject];
            DLog(@"Angle - %f",RADIANS_TO_DEGREES(endAngle))
            //path
            CGMutablePathRef path = CGPathCreateMutable();
            CGPathAddArc(path, NULL, prevPoint.x, prevPoint.y, radius, startAngle, endAngle, NO);
            CGPoint currentPoint = CGPathGetCurrentPoint(path);
            //find angles from start point to center and end point to center
            CGFloat angle1 = [self angleFromFirstPoint:prevPoint secondPoint:centerPoint];
            CGFloat angle2 = [self angleFromFirstPoint:CGPathGetCurrentPoint(path) secondPoint:centerPoint];
            double endX1 = cos(angle1) * LEVEL_WIDTH + prevPoint.x;
            double endY1 = sin(angle1) * LEVEL_WIDTH + prevPoint.y;

            double endX2 = cos(angle2) * LEVEL_WIDTH + CGPathGetCurrentPoint(path).x;
            double endY2 = sin(angle2) * LEVEL_WIDTH + CGPathGetCurrentPoint(path).y;

            //first connect current point to end2 point then draw arc and then connect to end1
            CGPathAddLineToPoint(path, NULL, endX2, endY2);
            //CGPathAddArcToPoint(path, NULL, CGPathGetCurrentPoint(path).x, CGPathGetCurrentPoint(path).y, endX2, endY2, radius - LEVEL_WIDTH);
            CGPathAddArc(path, NULL, CGPathGetCurrentPoint(path).x , CGPathGetCurrentPoint(path).y, radius - LEVEL_WIDTH, endAngle, startAngle, YES);

            //CGPathAddLineToPoint(path, NULL, endX1, endY1);

            //CGPathCloseSubpath(path);
            A3DonutChartShapeLayer* arcLayer = [[A3DonutChartShapeLayer alloc]init];
            arcLayer.path = path;
            //arcLayer.frame = CGPathGetBoundingBox(path);
            arcLayer.lineWidth = BORDER_WIDTH;
            arcLayer.strokeColor = [UIColor blackColor].CGColor;
            arcLayer.fillColor = [UIColor clearColor].CGColor;
            [self.layer addSublayer:arcLayer];

Any pointers?

Was it helpful?

Solution

here it is:

CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, rect.size.width/2, rect.size.height/2, 100, startAngle, endAngle, NO);
CGPathAddArc(path, NULL, rect.size.width/2, rect.size.height/2, 100-50, endAngle, startAngle, YES);
CGPathCloseSubpath(path);

OTHER TIPS

Option 1: CorePlot API Instead of drawing by self, and taking care of all the scenarios, I would prefer to use: CorePlot

Option 2: Google Chart API

NSString* myurl=@"http://chart.apis.google.com/chart?cht=pc&chd=t:120,45%7c120,60,50,70,60&chs=300x200&chl=%7c%7chelo%7cwrd%7cindia%7cpak%7cban&chco=FFFFFF%7cFFFFFF,e72a28%7ca9d331%7cffce08%7c8a2585%7c184a7d";

NSMutableURLRequest *theRequest=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:myurl] cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                    timeoutInterval:60.0];                                              

NSURLResponse* response;
NSError* error;
NSData *imageData=[NSURLConnection sendSynchronousRequest:theRequest returningResponse:&response error:&error];

UIImage *myimage = [[UIImage alloc] initWithData:imageData];

Option 3: Nice Tutorial for Core Graphics and ARC drawing

RayWenderlich Tutorial

Option 4: Using HTML + CSS + JavaScript

Add a UIWebView and display a local HTML page which will generate dynamic graph according to the values passed to JavaScript

A note on your actual question

Paths already do that. Lines, arcs, curves, etc. are all added from the current point. It looks like you are passing the current point for the x and y arguments in CGPathAddArc(...) but those arguments are used for the center of the arc, not the start.


A much better way of doing a donut chart.

That said, there is a much better way of doing donut charts. First you do the single arc in te center of the donut then you create a new path by stroking that arc. This allows you to customize the width of the donut very easily. I've described this and broken down the code in great detail in this answer but here is a shorter version of the shape itself.

Code to do this

Create the arc in the center of the donut (orange line in the below image)

enter image description here

CGMutablePathRef arc = CGPathCreateMutable();
CGPathMoveToPoint(arc, NULL,
                  startPoint.x, startPoint.y);
CGPathAddArc(arc, NULL,
             centerPoint.x, centerPoint.y,
             radius,
             startAngle,
             endAngle,
             YES);

Create a new path by stroking that path (dotted shape in the image above and filled shape in the image below):

enter image description here

CGFloat lineWidth = 10.0;
CGPathRef strokedArc =
    CGPathCreateCopyByStrokingPath(arc, NULL,
                                   lineWidth,
                                   kCGLineCapButt,
                                   kCGLineJoinMiter, // the default
                                   10); // 10 is default miter limit
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top