I am using a UIBezierPath to draw a curved line. The curve works however, I am unable to curve the edges of the line.
If you look at the top/bottom end of the curve, you can see that the edges are flattened out, thats not what I want. Is there a way to curve the edges?
I am using a simple UIBezierPath to draw an (almost) semi circle. This creates the curved line shape that I want:
CAShapeLayer *circle = [CAShapeLayer layer];
circle.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(0, 0) radius:70 startAngle:1.0472 endAngle:5.23599 clockwise:NO].CGPath;
circle.fillColor = [UIColor clearColor].CGColor;
circle.strokeColor = [UIColor colorWithWhite:1.0 alpha:0.2].CGColor;
circle.lineWidth = 18.0;
circle.cornerRadius = 10.0;
circle.position = CGPointMake(100, 100);
circle.anchorPoint = CGPointMake(0.5, 0.5);
[mainView.layer addSublayer:circle];
Despite setting the cornerRadius property, the corners are not curved. What am I doing wrong?
Thanks for your time, Dan.
Include the following.
circle.lineCap = kCALineJoinRound;
For Swift 3.0 and above
circle.lineCapStyle = .Round;
Swift 4.0
circle.lineCap = kCALineCapRound
and if you want your lines intersections to also be rounded set
circle.lineJoin = kCALineCapRound
as well.
Related
My problem is following: I'm trying to cut out a segment out of custom UIView and apply a shadow effect to this view.
In a custom UIView class, I did this:
Created two layers - shadow and mask. Added a shadow layer as a sublayer for this custom view. Then I created a new view, set its mask as the mask layer and added it as a subview to the custom view.
self.backgroundColor = [UIColor clearColor];
CGFloat radius = 40;
float startAngle = -M_PI;
float endAngle = 0;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.bounds];
[path moveToPoint:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMaxY(self.bounds)+radius/1.8)];
[path addArcWithCenter:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMaxY(self.bounds)+radius/1.8) radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
CAShapeLayer *shadowLayer = [[CAShapeLayer alloc] init];
shadowLayer.frame = self.bounds;
shadowLayer.path = path.CGPath;
shadowLayer.shadowColor = [UIColor grayColor].CGColor;
shadowLayer.shadowOpacity = 0.5;
shadowLayer.shadowRadius = 2;
shadowLayer.masksToBounds = NO;
shadowLayer.shadowOffset = CGSizeMake(2.0, 2.0);
shadowLayer.fillRule = kCAFillRuleEvenOdd;
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.masksToBounds = NO;
maskLayer.path = path.CGPath;
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.fillColor = [UIColor whiteColor].CGColor;
[self.layer addSublayer:shadowLayer];
UIView *view = [[UIView alloc] initWithFrame:self.bounds];
view.backgroundColor = [UIColor whiteColor];
view.layer.mask = maskLayer;
[self addSubview:view];
That is what I want to achieve -
and this is what I actually get
If I set clipToBounds = YES, it will cut off the desired shadow effect.
There would be absolutely no problem, if I wanted to cut of a semicircle. Because in this case the semicircle path fully sits inside of view bounds.
But I do need to achieve a result shown in the first image.
I was thinking about building the path line by line, but the problem occurs when it comes to this arc, and the result will probably not be so accurate.
Does anyone have ideas how it could be done?
Thank you!
EDIT:
If the issue is that the path is wrong, do no composite the path in code using a circle and guessing the coordinates. Instead you should export the coordinates actual artwork and use the artwork coordinates for the curve. It looks like you have a sketch file so the easiest way to do this is to copy and paste into paint code which will give you the code for the curve, but in the event you don't have paint code (you should, its amazing for this kind of stuff) you can export the curve as SVG from sketch. If you open the resulting SVG in a text editor you will see its in human readable XML and you can extract the control points from your Bezier curve from there.
I am not able to display rounded caps on my Arc created using UIBezierPath. It's still perfectly squared regardless I set kCGLineCapRound or not.
This topic should be same as this one, however solution is not working.
Here is the example code I have in viewWillAppear (for test purposes only):
int radius = 100;
CAShapeLayer *arc = [CAShapeLayer layer];
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 50) radius:radius startAngle:M_PI endAngle:M_PI/150 clockwise:YES];
path.lineCapStyle = kCGLineCapRound;
arc.path = path.CGPath;
arc.position = CGPointMake(CGRectGetMidX(self.view.frame)-radius, CGRectGetMidY(self.view.frame)-radius);
arc.fillColor = [UIColor clearColor].CGColor;
arc.strokeColor = [UIColor purpleColor].CGColor;
arc.lineWidth = 10.0f;
arc.cornerRadius = 3.0f;
Here is how it looks:
I am helpless so I would appreciate any help. Thanks guys.
Use the lineCap property of the CAShapeLayer rather than the lineCapStyle of the path.
arc.lineCap = kCALineCapRound;
If you are calling the path's stroke method (e.g. if you're doing this in drawRect or manually drawing in a UIGraphicsContext) then set attributes like the cap or join styles in the UIBezierPath. But when using CAShapeLayer, the attributes are set on the shape layer, not the path.
I've created a dashed CAShapeLayer using the code below. The line draws correctly when its path is on a perfectly horizontal plane. However as soon as the path moves up or down, the line suffers from some issues. See the images as an example.
CGMutablePathRef linePath = CGPathCreateMutable();
CGPathMoveToPoint(linePath, NULL, startShapeLayer.centerX, startShapeLayer.centerY);
CGPathAddLineToPoint(linePath, NULL, endShapeCenter.x, endShapeCenter.y);
CGPathCloseSubpath(linePath);
CAShapeLayer *lineLayer = [CAShapeLayer layer];
lineLayer.path = linePath;
lineLayer.fillColor = [UIColor clearColor].CGColor;
lineLayer.strokeColor = [UIColor whiteColor].CGColor;
lineLayer.lineCap = kCALineCapRound;
lineLayer.lineDashPattern = #[#5, #5];
lineLayer.lineDashPhase = 5.0;
lineLayer.lineWidth = 1.0;
If anyone has some advice about drawing these kinds of shapes it'd be great to hear.
I'd like to draw a circle without filling (only border of the circle) step by step (like animated timer). 1 spin is equal 1 day (24 hours). I really don't know what to do.
Steps I've made
1) I've tried https://github.com/danielamitay/DACircularProgress (it's too wide line of progress)
2) I've tried to draw a circle with many arcs.
Can you put me some code please. I really confused. Thanks in advance.
EDIT
I'd like to use NSTimer because I have a button which allow user to stop drawning a circle. If user touch a button again - drawning will have to continue.
What I would do is to create a path that is a circle and use that with a CAShapeLayer and animate the strokeEnd similar to what I did in this answer.
It would look something like this (but I didn't run this code so there may be typos and other mistakes):
UIBezierPath *circle = [UIBezierPath bezierPathWithArcCenter:center
radius:radius
startAngle:0
endAngle:2.0*M_PI
clockwise:YES];
CAShapeLayer *circleLayer = [CAShapeLayer layer];
circleLayer.bounds = CGRectMake(0, 0, 2.0*radius, 2.0*radius);
circleLayer.path = circle.CGPath;
circleLayer.strokeColor = [UIColor orangeColor].CGColor;
circleLayer.lineWidth = 3.0; // your line width
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = 10.0; // your duration
// Animate from no part of the stroke being drawn to the entire stroke being drawn
drawAnimation.fromValue = #0;
drawAnimation.toValue = #1;
Just note that both the path and the shape layer has a position so the circle path should be defined relative to the origin of the shape layers frame. It might be clearer to define the shape layer first and then create an oval inside of its bounds:
CAShapeLayer *circleLayer = [CAShapeLayer layer];
circleLayer.bounds = CGRectMake(0, 0, 2.0*radius, 2.0*radius);
circleLayer.position = center; // Set center of the circle
// Create a circle inside of the shape layers bounds
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:circleLayer.bounds];
circleLayer.path = circle.CGPath;
// Same appearance configuration as before
circleLayer.strokeColor = [UIColor orangeColor].CGColor;
circleLayer.lineWidth = 3.0; // your line width
If DACircleProgressenter link description here otherwise works for you, it looks like you can easily set the line thickness.
As opposed to have a simple lineWidth type property, it seems the author of that library sets the thickness based on a ratio to the radius of the circle. This exists as the thicknessRatio property of that class. For example, if your radius is 40, then setting thicknessRatio to 0.025 should yield a line width of 1. That library seems simple and well thought out - consider using it, or learning from it.
The default is set to 0.3, so a circle with a radius of 40 would have a progress line thickness of 12. That's probably what you were seeing.
Good luck!
I am trying to draw a view with few hollow circles in it. The view background color will be black with opacity 0.5 and hollow circles on places where I could see the view underneath it. This is working fine with below piece of code but has an issue when my hollow circles intersects, I want to cover both of them as hollow area but due to even odd rule this is not working out. Any suggestions?
Or any alternatives?
- (void)addShadowView {
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height) cornerRadius:0];
for (NSValue *point in self.hollowFrames) {
UIBezierPath *circlePath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(point.CGPointValue.x - self.hollowCircleRadius.floatValue, point.CGPointValue.y - self.hollowCircleRadius.floatValue, 2.0 * self.hollowCircleRadius.floatValue, 2.0 * self.hollowCircleRadius.floatValue) cornerRadius:self.hollowCircleRadius.floatValue];
[path appendPath:circlePath];
}
[path setUsesEvenOddFillRule:YES];
CAShapeLayer *fillLayer = [CAShapeLayer layer];
fillLayer.path = path.CGPath;
fillLayer.fillRule = kCAFillRuleEvenOdd;
fillLayer.fillColor = [UIColor blackColor].CGColor;
fillLayer.opacity = 0.5;
[self.layer addSublayer:fillLayer];
}
This is how it looks right now. I want the intersected area also to be hollow and not filled with the fillColor.
don't fill the circles, clip out the centers.