Draw arc segments using UIBezierPath

iphonic picture iphonic · May 26, 2015 · Viewed 7.3k times · Source

I know there are lots of example related to UIBezierPath, but I am very confused about the angles to use to draw arc, my requirement is like the image below to draw arcs. The length and angle should be calculated as per total number.

enter image description here

Right now I have created a function which draws the arcs see below

-(void)createCircleWithStartAngle:(int)startAngle endAngle:(int)endAngle name:(NSString *)name{

    UIView *subView=_viewContainer;

    // Set up the shape of the circle
    int radius = 125;

    CAShapeLayer *circle = [CAShapeLayer layer];
    // Make a circular shape

    UIBezierPath *path=[UIBezierPath bezierPathWithArcCenter:CGPointMake(radius, radius) radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];


    circle.path = [path CGPath];


    // Center the shape in self.view
    circle.position = CGPointMake(CGRectGetMidX(subView.bounds)-radius,
                                  CGRectGetMidY(subView.bounds)-radius);


    circle.fillColor = [UIColor clearColor].CGColor;

    //making line end cap round
    circle.lineCap=kCALineCapRound;


    UIColor *strokeColor;
    if([name isEqualToString:@"A"])
        strokeColor=[UIColor darkGrayColor];
    else if([name isEqualToString:@"B"])
        strokeColor=[UIColor colorWithRed:40.0/255.0 green:180.0/255.0 blue:213.0/255.0 alpha:1.0];
    else if([name isEqualToString:@"C"])
        strokeColor=[UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1.0];


    circle.strokeColor = strokeColor.CGColor;
    circle.lineWidth = 10;

    // Add to parent layer
    [subView.layer addSublayer:circle];



    // Configure animation
    CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    drawAnimation.duration            = 1.0;
    drawAnimation.repeatCount         = 1.0;


    drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
    drawAnimation.toValue   = [NSNumber numberWithFloat:1.0f];


    drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];

    // Add the animation to the circle
    [circle addAnimation:drawAnimation forKey:@"drawCircleAnimation"];
}

I am calling the above function 3 times so it creates 3 arc segments like below

    float angleToStart=DEGREES_TO_RADIANS(120);
    float angleToEnd=DEGREES_TO_RADIANS(270);
    //investment
    [self createCircleWithStartAngle:angleToStart endAngle:angleToEnd name:@"A"];



    float factor=total/bvalue;
    angleToStart=angleToEnd;
    angleToEnd=180/factor;
    angleToEnd=DEGREES_TO_RADIANS(angleToEnd);

    [self createCircleWithStartAngle:angleToStart endAngle:angleToEnd name:@"B"];



    angleToStart=DEGREES_TO_RADIANS(angleToEnd);
    angleToEnd=DEGREES_TO_RADIANS(60);
    [self createCircleWithStartAngle:angleToStart endAngle:angleToEnd name:@"C"];

as a result I am getting output like below

enter image description here

which is somehow correct, but not 100%, scenarios as below

  1. The starting point in dark gray is not same as endpoint off white
  2. The dark gray portion should be half circled always, and blue should follow it with factor.
  3. The off white segement to fill the rest portion to the end.

Can anyone help me to get the values correct? So that it draws

Thanks.


Update(Adding complete solution to draw similar arc)

The function below responsible for drawing the arc for supplied angles

-(void)createCircleWithStartAngle:(float)startAngle endAngle:(float)endAngle chartSection:(ChartSection)section lineCap:(NSString *const)lineCap{

    UIView *subView=_circleView;

    // Set up the shape of the circle
    float radius = _circleView.bounds.size.width/2.0;//125.0;

    CAShapeLayer *circle = [CAShapeLayer layer];
    // Make a circular shape

    UIBezierPath *path=[UIBezierPath bezierPathWithArcCenter:CGPointMake(radius, radius) radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];



    circle.path = [path CGPath];


    // Center the shape in self.view
    circle.position = CGPointMake(CGRectGetMidX(subView.bounds)-radius,
                                  CGRectGetMidY(subView.bounds)-radius);

    // Configure the apperence of the circle
    circle.fillColor = [UIColor clearColor].CGColor;


    UIColor *strokeColor;

    circle.lineCap=lineCap;

    switch (section) {
        case ChartSectionA:
            strokeColor=[UIColor darkGrayColor];
            break;
        case ChartSectionB:
            strokeColor=[UIColor colorWithRed:40.0/255.0 green:180.0/255.0 blue:213.0/255.0 alpha:1.0];
            break;
        case ChartSectionC:
            strokeColor=[UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1.0];
            break;
        default:
            strokeColor=[UIColor colorWithRed:40.0/255.0 green:180.0/255.0 blue:213.0/255.0 alpha:1.0];
            break;
    }



    circle.strokeColor = strokeColor.CGColor;
    circle.lineWidth = 10;

    //keep the arrangement intact with the indexes..
    [subView.layer insertSublayer:circle atIndex:section];



    // Configure animation
    CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    drawAnimation.duration            = 1.0;
    drawAnimation.repeatCount         = 1.0;

    // Animate from no part of the stroke being drawn to the entire stroke being drawn
    drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
    drawAnimation.toValue   = [NSNumber numberWithFloat:1.0f];

    // Experiment with timing to get the appearence to look the way you want
    drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];

    // Add the animation to the circle
    [circle addAnimation:drawAnimation forKey:@"drawCircleAnimation"];
}

The function below calculates angle as per values and supplies to the function above..

-(void)createArcWithValueOfSectionA:(float)amountA sectionB:(float)amountB sectionC:(float)amountC{

    if(amountA<0.0)
        amountA=0.0;

    if(amountB<0.0)
        amountB=0.0;

    if(amountC<0.0)
        amountC=0.0;


    float baseStartAngle=120.0;//base start angle
    float baseEndAngle=60.0;//base end angle
    float totalAngle=300.0;//total angle to be calculated

    float sectionAPercent = amountA/(amountA+amountB+amountC);
    float sectionBPercent = amountB/(amountA+amountB+amountC);


    float angleToStart=DEGREES_TO_RADIANS(baseStartAngle);
    float angleToEnd=0.0;

    if(sectionAPercent<0)
        sectionAPercent=0.0;

    if(sectionBPercent<0)
        sectionBPercent=0.0;


    //calculate sectionA end angle with the value inputted
    angleToEnd=baseStartAngle + sectionAPercent * totalAngle;

    //we need to determine cap style as per data
    NSString *lineCap=kCALineCapRound;



    //Section A
    [self createCircleWithStartAngle:angleToStart endAngle:DEGREES_TO_RADIANS(angleToEnd) chartSection:ChartSectionA lineCap:lineCap];


    //next drawing for bidding should start from investment end angle
    angleToStart=angleToEnd;

    angleToEnd=sectionBPercent * totalAngle;

    angleToEnd=angleToStart+angleToEnd;

    lineCap=kCALineCapSquare;

    //Section B
    [self createCircleWithStartAngle:DEGREES_TO_RADIANS(angleToStart) endAngle:DEGREES_TO_RADIANS(angleToEnd) chartSection:ChartSectionB lineCap:lineCap];

    //next drawing for sectionC should start from sectionB end angle
    angleToStart=angleToEnd;


    lineCap=kCALineCapRound;

    //draw Section C amount till end
    angleToEnd=DEGREES_TO_RADIANS(baseEndAngle);
    [self createCircleWithStartAngle:DEGREES_TO_RADIANS(angleToStart) endAngle:angleToEnd chartSection:ChartSectionC lineCap:lineCap];

}

Hope it helps someone.

Answer

Rob picture Rob · May 26, 2015

The problem is that you're using integers for your angles, but you're dealing with radians:

- (void)createCircleWithStartAngle:(int)startAngle endAngle:(int)endAngle name:(NSString *)name;

Use floating point (e.g. CGFloat) for your angles.