Draw circle with UIBezierPath
Asked Answered
D

3

15

I'm trying to draw a circle using UIBezierPath addArcWithCenter method :

UIBezierPath *bezierPath = 
  [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0., 0., 100., 100.)];

[bezierPath addArcWithCenter:center 
                      radius:0. 
                  startAngle:0 endAngle:2 * M_PI clockwise:YES];

CAShapeLayer *progressLayer = [[CAShapeLayer alloc] init];

[progressLayer setPath:bezierPath.CGPath];
[progressLayer setStrokeColor:[UIColor colorWithWhite:1. alpha:.2].CGColor];
[progressLayer setFillColor:[UIColor clearColor].CGColor];
[progressLayer setLineWidth:.3 * self.bounds.size.width];
[progressLayer setStrokeStart:_volumeValue/100.];
[progressLayer setStrokeEnd:volume/100.]; // between 0 and 100

[_circleView.layer addSublayer:progressLayer];

but what I get is the following :

enter image description here

I tried to play with the different parameters but no luck

Thank you

UPDATE :

I'm sorry if I didn't explain what I'm trying to do:

*The background circle is drawed using :

[UIBezierPath bezierPathWithOvalInRect:CGRectMake(0., 0., 100., 100.)]

*I'm trying to draw the red circle step by step using bezierPathWithOvalInRect between 2 values : _volumeValue and volume

But I can't get a perfect circle, instead I get the horizontale part after certain value.

enter image description here

Dialogue answered 23/9, 2014 at 10:25 Comment(5)
Which bit is the bit that isn't working? To me it looks like a circle. Can you remove the extraneous "noise" from the image to show just the stuff you are interested in?Parthenia
Also, what are volume and _volumeValue?Parthenia
It's not clear what you are trying to do.Darvon
Much better, I think I've spotted it. Added answer. :)Parthenia
https://mcmap.net/q/567953/-why-does-giving-addarcwithcenter-a-startangle-of-0-degrees-make-it-start-at-90-degreesRenee
P
33

OK, try this...

UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath addArcWithCenter:center radius:50 startAngle:0 endAngle:2 * M_PI clockwise:YES];

CAShapeLayer *progressLayer = [[CAShapeLayer alloc] init];
[progressLayer setPath:bezierPath.CGPath];
[progressLayer setStrokeColor:[UIColor colorWithWhite:1.0 alpha:0.2].CGColor];
[progressLayer setFillColor:[UIColor clearColor].CGColor];
[progressLayer setLineWidth:0.3 * self.bounds.size.width];
[progressLayer setStrokeEnd:volume/100];
[_circleView.layer addSublayer:progressLayer];

volume should be between 0 and 100.

Also, you were creating a path with an ellipse and then adding an arc to it. Don't do that. Just add the arc to the empty path.

If you want to change where the arc starts from then change the startAngle and endAngle when adding the arc. Don't change the stroke start value.

Animating change

[CATransaction begin];
CABasicAnimation *animateStrokeDown = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animateStrokeDown.toValue = [NSNumber numberWithFloat:volume/100.];
[progressLayer addAnimation:animateStrokeDown forKey:@"animateStrokeDown"];
[CATransaction commit];

OK, what this will do is animate the strokeEnd property of the path. You have to realise though, it works like this...

Begin state: start = 0, end = 6
0123456
-------

// increase volume
Animate to: end = 9
0123456789
----------

// decrease volume
Animate to: end = 1
01
--

The start has not moved. The end has moved. You are not "filling in" the rest of the line. You are changing the line.

This is why the start should always be 0 and you just change the stroke end.

Parthenia answered 23/9, 2014 at 11:42 Comment(8)
Adding the arc to an empty path helped but why not specify the strokeStart and strokeEnd ? As I want to animate the change between those 2 values, and start from the strokeEnd point in the next call.Dialogue
Here is the code for the animation : [CATransaction begin]; CABasicAnimation *animateStrokeDown = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; animateStrokeDown.fromValue = [NSNumber numberWithFloat:_volumeValue/100.]; animateStrokeDown.toValue = [NSNumber numberWithFloat:volume/100.]; animateStrokeDown.removedOnCompletion = YES; [progressLayer addAnimation:animateStrokeDown forKey:@"animateStrokeDown"]; [CATransaction commit];Dialogue
Hmm... Are you trying to have a volume slider thing? So it will show a full circle if full volume and a half circle at half volume etc? Surely the beginning of the circle should never move? Ahh! I think I get it one sec...Parthenia
yes exactly a circular volume slide, with animating volume change :)Dialogue
@Dialogue take a look. You had the animation right. Just not the drawing of the arc.Parthenia
and for decreasing volume, I try to grab the last layer added and set the strokeColor to clearColor: progressLayer = _circleView.layer.sublayers[_circleView.layer.sublayers.count - 1]; [progressLayer setStrokeColor:[UIColor clearColor].CGColor]; but I don't think that's the best approach ..Dialogue
@Dialogue OK, no. You only need one layer. It has the entire circle in it. You change that circle. There should only ever be one layer. i.e. one layer, one circle, animate the endpoint of that circle.Parthenia
@Parthenia I am trying to change the start angle to 3pi*/4 and obtain a circle . If I set start angle as 0 and end angle as 2*pi then I am getting the circle. But If I use 3*pi/4 as start angle it's not what will be the end angle?Lampas
U
1
startAngle:degreesToRadians(-90) endAngle:0 clockwise:YES

(The reason is that the default coordinate system on iOS has a x-axis pointing to the right, and a y-axis pointing down.)

read this document https://developer.apple.com/library/ios/documentation/uikit/reference/UIBezierPath_class/index.html

specifying a start angle of 0 radians, an end angle of π radians, and setting the clockwise parameter to YES draws the bottom half of the circle. However, specifying the same start and end angles but setting the clockwise parameter set to NO draws the top half of the circle.

Uralaltaic answered 23/9, 2014 at 10:55 Comment(1)
This does not result in a circle.Darvon
D
1

I have no idea what you expect and what's wrong with your actual result—but a radius of 0 in your second line of code seems fishy. It just adds a point at center to the current path (which already includes a circle).

Darvon answered 23/9, 2014 at 11:1 Comment(1)
you are perfectly right, the logical value of radius is 50., but oddly even if I change that value I always get the same result.Dialogue

© 2022 - 2024 — McMap. All rights reserved.