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
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!
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.
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 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).
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.