Can't remove CAShapeLayer from Super Layer - ios

I'm trying to remove my circle that I've created as a CAShapeLayer and drawn with UIBezier path if I can't receive a measurement. In my (void)drawRect method for my UIView class, I create and draw a circle if a measurement is found. However, if there is no measurement I'd like to get rid of the circle. For some reason I can't.
Here is the code that creates the circle in -(void)drawRect
- (void)drawRect:(CGRect)rect {
CAShapeLayer *grayCircle = [CAShapeLayer layer];
CAShapeLayer *progressArc = [CAShapeLayer layer];
DBHelper *dbHelper = [DBHelper getSharedInstance];
if ([dbHelper getPairedDevice]==nil) {
[grayCircle removeFromSuperlayer]; // I want to remove the grayCirle if no measurement is found AKA getPairedDevice = nil
}
....
if (_latestMeasurement) {
...
// Gray outer circle
UIBezierPath *grayCirclePath=[UIBezierPath bezierPathWithArcCenter:CGPointMake(radius, radius) radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
grayCircle.path = [grayCirclePath CGPath];
grayCircle.position = CGPointMake(realBounds.origin.x, realBounds.origin.y);
grayCircle.fillColor = [UIColor clearColor].CGColor;
grayCircle.lineCap=kCALineCapRound;
grayCircle.strokeColor = [UIColor grayColor].CGColor;
grayCircle.lineWidth = 30;
// Progress arc
UIBezierPath *progressPath=[UIBezierPath bezierPathWithArcCenter:CGPointMake(radius, radius) radius:radius startAngle:startAngle endAngle:patientOutput clockwise:YES];
progressArc.path = [progressPath CGPath];
progressArc.position = CGPointMake(grayCircle.frame.origin.x, grayCircle.frame.origin.y);
progressArc.fillColor = [UIColor clearColor].CGColor;
progressArc.lineCap=kCALineCapRound;
progressArc.lineWidth = 30;
[self.layer addSublayer:grayCircle];
[self.layer addSublayer:progressArc];
}
So here's the issue: The following lines of code aren't removing the gray circle:
if ([dbHelper getPairedDevice]==nil) {
[grayCircle removeFromSuperlayer];
}
I'm wondering why is that the case, and how can I remove it from the view's sublayer?

You are creating a new reference to grayCircle each time you go through drawRect. When you remove it - you're removing the new one, not the one you drew previously.
If you already have it, you need to find it first, then remove it - going through the subViews, or maintain a reference to it at the class level

Related

Draw Line and get actions between sections

I want to draw lines which is shown in the image.
and I have to add four labels with name Section 1,Section 2,Section 3,Section 4.
I have tried below code to draw lines and it works
-(UIBezierPath*)createBezierPathWithMoveToPoint:(CGPoint)moveToPoint andlineToPoint:(CGPoint)toPoint
{
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:moveToPoint];
[path addLineToPoint:toPoint];
return path;
}
-(CAShapeLayer*)createLayerWithPath:(CGPathRef)path strokColor:(UIColor *)colorStroke lineWidth:(CGFloat)width fillColor:(UIColor *)colorFill
{
CAShapeLayer *layer = [CAShapeLayer layer];
layer.path = path;
layer.strokeColor = [colorStroke CGColor];
layer.lineWidth = width;
layer.fillColor = [colorFill CGColor];
return layer;
}
By using above code I have achieved lines and I have added for UIlabel's manually. Can anyone please help me to get action like UIButton actions, when user touch in between these sections. ?
OR
If there is any best and easiest way to achieve this please let me know.

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.

Drawing a line with Bezierpath using CAShapeLayer object

I am making an image editor which can create different shapes objects like circle, triangle and square which can also be updated or removed. So I have used CAShapeLayer for creating shapes objects.
Now I also want to draw a line on image which can also be updated or removed so I have used bezierpath and CAShapeLayer to create the line, it is working fine. BUT now the problem is that when I want to select any existing line it can be selected any where close to line tool because CAShapeLayer also set the fill region which will be a straight line from start point to end point.
My question is that how can I create line with no fill region using CAShapeLayer.
Here is my code for creating line:
CAShapeLayer *line = [CAShapeLayer layer];
// Using bezierpath to make line
UIBezierPath *linePath=[UIBezierPath bezierPath];
// Creating L with line
[linePath moveToPoint:point1];
[linePath addToPoint:point2];
[linePath addToPoint:point3];
line.path=linePath.CGPath;
// Configure the appearence of the line
line.fillColor = Nil;
line.opacity = 1.0;
line.strokeColor = [UIColor whiteColor].CGColor;
Any idea on this will be really appreciated.
Can you try this. Its work for me
CAShapeLayer *line = [CAShapeLayer layer];
UIBezierPath *linePath=[UIBezierPath bezierPath];
[linePath moveToPoint:CGPointMake(startx, starty)];
[linePath addLineToPoint:CGPointMake(endx, endy)];
line.lineWidth = 10.0;
line.path=linePath.CGPath;
line.fillColor = shapecolor.CGColor;
line.strokeColor = shapecolor.CGColor;
[[self.view layer] addSublayer:line];
I understand you I experienced this issue as well try this:
GPathRef linePathRef = linePath.CGPath
linePathRef = CGPathCreateCopyByStrokingPath(linePathRef, NULL, line.lineWidth, kCGLineCapRound, kCGLineJoinRound, 1);
BOOL pathContainsPoint = CGPathContainsPoint(linePathRef, NULL, touchLocation, NO);
if(pathContainsPoint){
//Do something with the cashapelayer line...
}else{
//Do something here if needed...
}

Resources