I want to draw a filled sector of a circle using SKShapeNode with UIBezierPath.
I works well in most cases, but with smaller angles the shape is not filled entirely(right under the arc there is a little gap). Like this:
Here is the code I use:
CGPoint center = CGPointMake(500, 300) ;
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath addArcWithCenter:center radius:400 startAngle:1.825777 endAngle:2.011118 clockwise:YES];
[bezierPath addLineToPoint:center];
[bezierPath closePath];
SKShapeNode *shapeNode = [SKShapeNode shapeNodeWithPath:bezierPath.CGPath];
shapeNode.strokeColor = [UIColor whiteColor];
shapeNode.fillColor = [UIColor whiteColor];
[self addChild:shapeNode];
The result is the same on iPad and on the simulator (iOS 8.1).
Am I doing something wrong or it is some limitation or bug?
If it's not possible to draw this shape more accurately with these API, could you please suggest some other ways to draw it in the context of SpriteKit game.
Thank you in advance.
It could be a SKShapeNode bug, see http://sartak.org/2014/03/skshapenode-you-are-dead-to-me.html
One fix, would be to increase the lineWidth of the SKShapeNode so it fills up the gap.
shapeNode.lineWidth = 4
You could instead do the same with UIKit using the same bezierPath and fill and stroke it in drawRect.
Related
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.
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 have used both UIBezierPath and CAShapeLayer before. But almost every time in conjunction with filling the object contained within the path with color inside. But I would like this time to fill the color outside of the object contained by the UIBezierPath.
I just wrote and ran the following simple code trying to get myself acquainted with the fillRule property:
CAShapeLayer *myLayer = (CAShapeLayer *) self.layer; //size: 320 X 480
UIBezierPath *testPath = [UIBezierPath bezierPathWithOvalInRect:(CGRect){{100, 100}, 100, 100}]; //a simple circle
myLayer.fillRule = kCAFillRuleNonZero; // have tried this as well: kCAFillRuleEvenOdd;
myLayer.path = testPath.CGPath;
myLayer.fillColor = [UIColor whiteColor].CGColor;
But the color is filled nonetheless inside. What I would like to find out is, how can I fill the color outside of the path? If I am using fillRule wrong here, I would like to know if there is other methods that can achieve this. Thanks in advance.
The main problem is that you can't really fill the outside of a shape, since there's no generic way to define what that means. What you need to do is first plot a path around the "outside" of your shape, and then add the circle as a subpath. How you do that depends on which fill rule you want to use. EvenOdd is the easiest:
CAShapeLayer *myLayer = (CAShapeLayer *) self.layer;
UIBezierPath *testPath = [UIBezierPath bezierPathWithRect:self.bounds];
[testPath appendPath:[UIBezierPath bezierPathWithOvalInRect:(CGRect){{100, 100}, 100, 100}]];
myLayer.fillRule = kCAFillRuleEvenOdd;
myLayer.path = testPath.CGPath;
myLayer.fillColor = [UIColor whiteColor].CGColor;
NonZero is a little bit harder because you have to force the path to be counter-clockwise which isn't an option for most of the UIBezierPath convenience methods:
CAShapeLayer *myLayer = (CAShapeLayer *) self.layer;
UIBezierPath *testPath = [UIBezierPath bezierPathWithRect:self.bounds];
UIBezierPath *counterClockwise = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:100 startAngle:0 endAngle:M_PI clockwise:NO];
[counterClockwise appendPath:[UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:100 startAngle:M_PI endAngle:0 clockwise:NO]];
[testPath appendPath:counterClockwise];
myLayer.fillRule = kCAFillRuleNonZero;
myLayer.path = testPath.CGPath;
myLayer.fillColor = [UIColor redColor].CGColor;
Depending on how you're constructing your actual path it may not make a difference either way.
If you haven't seen it already, the winding rules documentation has some nice diagrams that I find helpful.
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.
This question already has an answer here:
How to Animate a UIBezierPath
(1 answer)
Closed 9 years ago.
Is is possible to animate a point of a bezier curve? I am trying make a smooth transition from a line to an arrow.
Here's what the line looks like in code
//// Color Declarations
UIColor* white = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 0.374];
//// Group
{
//// Bezier Drawing
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(30.5, 43.5)];
[bezierPath addLineToPoint: CGPointMake(30.5, 29.59)];
[bezierPath addLineToPoint: CGPointMake(30.5, 15.5)];
bezierPath.lineCapStyle = kCGLineCapRound;
bezierPath.lineJoinStyle = kCGLineJoinBevel;
[white setStroke];
bezierPath.lineWidth = 5.5;
[bezierPath stroke];
}
... however I do not know how to pick a point and animate just that. Is this even possible?
Explicit CGPath animation using a CAShapeLayer:
// Create the starting path. Your curved line.
UIBezierPath * startPath;
// Create the end path. Your straight line.
UIBezierPath * endPath;
// Create the shape layer to display and animate the line.
CAShapeLayer * myLineShapeLayer = [[CAShapeLayer alloc] init];
CABasicAnimation * pathAnimation = [CABasicAnimation animationWithKeyPath:#"path"];
pathAnimation.fromValue = (__bridge id)[startPath CGPath];
pathAnimation.toValue = (__bridge id)[endPath CGPath];
pathAnimation.duration = 5.0f;
[myLineShapeLayer addAnimation:pathAnimation forKey:#"animationKey"];
It isn't clear what you are asking. Are you trying to animate changing the shape of your line by moving one of the control points?
The way to animate changes to paths is to create a CAShapeLayer and install it as a sublayer of your view's layer. Then if you change the path associated with the shape layer the system will use implicit animation to make the change.
Note that the path in the shape layer needs to have the same number/type of control points or the results of the animation are "undefined."