Filling color in intersected path with UIBezierPath - ios

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.

Related

Cutting a layer out of a layer

So I'm trying to cut a circle out of an existing layer; and here's my code:
CGRect bounds = _containerLayer.bounds;
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = bounds;
maskLayer.fillColor = [UIColor blackColor].CGColor;
CGFloat smallCircleRadius = (frame.size.width - barWidth)/2;
CGRect smallCircleRect = CGRectMake(CGRectGetMidX(bounds) - smallCircleRadius, CGRectGetMidY(bounds) - smallCircleRadius, 2 * smallCircleRadius, 2 * smallCircleRadius);
UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:smallCircleRect];
[circlePath appendPath:[UIBezierPath bezierPathWithRect:bounds]];
maskLayer.path = circlePath.CGPath;
maskLayer.fillRule = kCAFillRuleEvenOdd;
_containerLayer.mask = maskLayer;
This code does work, as I now see a black rectangle with a hollow circle in the middle. But the problem is that when I tried to set maskLayer.fillColor = [UIColor clearColor].CGColor;, The whole thing including _containerLayer just disappears - I want to make the fillColor transparent so I can see what's originally in the _containerLayer (which wasn't cut out). What should I do to achieve this? Thanks!

iOS draw Stroked Arc

I'm trying to draw filled and stroked arc. Please see this image(Left: what i have. Right: what i need]):
I don't know how to set arcs join behaviour. Here is the code(arrowLayer is a CAShapeLayer):
UIBezierPath *arcPath = [UIBezierPath bezierPath];
[arcPath addArcWithCenter:CGPointMake(frame.size.width/2, frame.size.height/2)
radius:65
startAngle:DEGREES_TO_RADIANS(-10)
endAngle:DEGREES_TO_RADIANS(320)
clockwise:YES];
[arcPath addArcWithCenter:CGPointMake(frame.size.width/2, frame.size.height/2)
radius:55
startAngle:DEGREES_TO_RADIANS(-10)
endAngle:DEGREES_TO_RADIANS(320)
clockwise:YES];
arrowLayer.path = [arcPath CGPath];
arrowLayer.fillColor = [UIColor clearColor].CGColor;
arrowLayer.strokeColor = [[UIColor redColor] CGColor];
arrowLayer.lineWidth = 1.0;
Additional Question:
How to get start and end points coords of arc, is it possible? It needs to complete the second part of my task.

Not able to round caps in UIBezierPath Arc even with kCGLineCapRound

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.

How can i remove the circle drawn in iOS?

I have this code where I draw circles on the screen, and I want to remove just the last circle drawn. What can I do? The code is set to draw a circle when I tap twice. I want to remove the last circle drawn when I tap one time.
- (UIBezierPath *)makeCircleAtLocation:(CGPoint)location radius:(CGFloat)radius {
iOSCircle *circle = [[iOSCircle alloc] init];
circle.circleCenter = location;
circle.circleRadius = radius;
[totalCircles addObject:circle];
UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:circle.circleCenter
radius:circle.circleRadius
startAngle:0.0
endAngle:M_PI * 2.0
clockwise:YES];
return path;
}
- (IBAction) tapEvent: (UIGestureRecognizer *) sender
{
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [[self makeCircleAtLocation:location radius:2.5] CGPath];
shapeLayer.strokeColor = [[UIColor redColor] CGColor];
//shapeLayer.fillColor = nil;
shapeLayer.lineWidth = 2.5;
// Add CAShapeLayer to our view
[self.view.layer addSublayer:shapeLayer];
// Save this shape layer in a class property for future reference,
// namely so we can remove it later if we tap elsewhere on the screen.
self.circleLayer = shapeLayer;
}
}
Create your circle in a distinct CAShapeLayer layer using a CGPath, and add it as a sublayer of your view.layer. That way, you will have total control over that circle (showing or hiding it).

iOS: UIBezierPath and CAShapeLayer fillRule

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.

Resources