UIBezierPaths are not kept on screen - ios

I have a custom UIView and in the drawRect method I create a UIBezierPath and render it to the screen. The issue I am having is that the previous UIBezierPath is removed from the view on the next drawRect call?
How can I keep all of these UIBezierPaths on screen?
- (void)drawRect:(CGRect)rect
{
UIBezierPath *path = [UIBezierPath bezierPath];
// Move to centre and draw an arc.
[path moveToPoint:self.center];
[path addArcWithCenter:self.center
radius:self.radius
startAngle:self.startAngle
endAngle:self.endAngle
clockwise:YES];
[path closePath];
path.usesEvenOddFillRule = YES;
[self.colorToRender setFill];
[path fill];
}

Whenever drawRect: is called you have to draw the whole screen of content. You can't append new drawings to what's already there. So you need to store the whole list of beziers and draw all of them.
Store them in an array and then loop over the array and draw each.
Also, you shouldn't really be creating new beziers in drawRect:...

I am not sure if this is the most convenient way but you have to keep reference to old paths and then combine the new one with them.
CAShapeLayer * shapeLayer; //Sublayer of your view
CGMutablePathRef combinedPath = CGPathCreateMutableCopy(shapeLayer.path);
CGMutablePathRef linePath = CGPathCreateMutable();
//Create your own custom linePath here
//No paths drawn before
if(combinedPath == NULL)
{
combinedPath = linePath;
}
else
{
CGPathAddPath(combinedPath, NULL, linePath);
}
shapeLayer.path = combinedPath;
CGPathRelease(linePath);

Related

Can't remove CAShapeLayer from Super Layer

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

Drawing 100+ circles with different position using UIBezierPaths

I am trying to draw 100+ UIBezierPath circles in the drawRect function of an UIView. I am creating and adding the UIBezierPaths to an array:
for (int i = 0; i < 100; i++) {
UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:centerPoint radius:10 startAngle:0 endAngle:360 clockwise:YES];
path.lineWidth = 2;
[bezierPaths addObject:path];
}
before calling the setNeedDisplay. Then in drawRect I iterate through the array
- (void)drawRect:(CGRect)rect {
for (UIBezierPath *path in bezierPaths) {
[path fill];
[path stroke];
}
}
This looks like an overkill, is there a better way of doing this? Using Profile I can see that the 86% of the MainThread is being used on calling the [path stroke] in drawRect.
I am passing 0 and 360 degrees as opposed to radiants and it is drawing them too many times.

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

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...
}

Joined UIBezierPath stroke as a whole

Being a beginner in iOS drawing I wanted to draw a small tooltip container which consists of a rectangle with an indicator arrow.
I created 2 UIBezierPaths and joined them with "appendPath".
I thought this was the right way to go about this, but after struggling for 1 day, I had to ask for help.
Here's the screenshot of where I am right now:
As you can see the problem is simply that when I try to stroke the joined path, it doesn't gets stroked as a whole, but as separate pieces.
Maybe someone could point me in the right direction to solve this ?
Here's the code:
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// create indicator path
UIBezierPath *indicator = [[UIBezierPath alloc] init];
// corner radius
CGFloat radius = self.cornerRadius;
// main path
UIBezierPath *path;
// format frame
rect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height - self.indicatorSize.height);
// assign path
path = [UIBezierPath bezierPathWithRoundedRect: rect
byRoundingCorners:UIRectCornerAllCorners
cornerRadii:CGSizeMake(radius, radius)];
// x point
CGFloat x = (rect.size.width - self.indicatorSize.width) / 2;
[indicator moveToPoint:CGPointMake(x, rect.size.height)];
[indicator addLineToPoint:CGPointMake(x + self.indicatorSize.width / 2, rect.size.height + self.indicatorSize.height)];
[indicator addLineToPoint:CGPointMake(x + self.indicatorSize.width, rect.size.height)];
// close path
[indicator closePath];
// append indicator to main path
[path appendPath:indicator];
[[UIColor redColor] setStroke];
[path setLineWidth:2.0];
[path stroke];
[self.fillColor setFill];
[path fill];
// release indicator
[indicator release];
}
Thanks in advance
You need to draw it all as one path, because at the moment you're telling it you want to draw a whole rectangle, and then a triangle, and that's what it's doing.
From the Apple UIBezierPath documentation:
[appendPath:] adds the commands used to create the path in bezierPath to the end of the receiver’s path. This method does not explicitly try to connect the subpaths in the two objects, although the operations in bezierPath might still cause that effect.
So first it will draw your rectangle, and then it will draw your triangle without attempting to join them

Resources