I am using a UIbezierpath to draw for my Project. I am using colors to draw. When I draw on same points, the color becomes darken.(i.e drawing on the same place will normally). I want to maintain the brush color though on overwrite.
Any help will be appreciated!!!!
Let's try below method,
- (void)drawRect:(CGRect)rect
{
CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(),self.backgroundColor.CGColor);
CGContextFillRect(UIGraphicsGetCurrentContext(), rect); // This line will clear your existing drawing
// Line Drawing code
}
OR
UIBezierPath * path = [UIBezierPath bezierPathWithRect:(CGRect){CGPointZero, newSize}]; // newSize will be your size of the Eraser
[[UIColor clearColor] setFill];
[path fill];
Please refer this link as well to make paint and erase.
Thanks!
Related
I want to make custom drawing so that i could convert it to image.
i have heard of UIBezierPath but donot know much about it, my purpose is to change color of it on basis of user's selection of color.
Create a CGGraphcisContext and get an image like this:
UIGraphicsBeginImageContextWithOptions(bounds.size, NO , [[UIScreen mainScreen] scale]);
// set the fill color (UIColor *)
[userSelectedColor setFill];
//create your path
UIBezierPath *path = ...
//fill the path with your color
[path fill];
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
You might have to combine multiple paths to get your desired shape. First create the 'drop' with bezier paths. The path might look something like this:
//Create the top half of the circle
UIBezierPath *drop = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetWidth(bounds)*0.5f, CGRectGetWidth(bounds)*0.5f)
radius:CGRectGetWidth(bounds)*0.5f
startAngle:0
endAngle:DEGREES_TO_RADIANS(180)
clockwise:NO];
//Add the first half of the bottom part
[drop addCurveToPoint:CGPointMake(CGRectGetWidth(bounds)*0.5f,CGRectGetHeight(bounds))
controlPoint1:CGPointMake(CGRectGetWidth(bounds),CGRectGetWidth(bounds)*0.5f+CGRectGetHeight(bounds)*0.1f)]
controlPoint2:CGPointMake(CGRectGetWidth(bounds)*0.6f,CGRectGetHeight(bounds)*0.8f)];
//Add the second half of the bottom part beginning from the sharp corner
[drop addCurveToPoint:CGPointMake(0,CGRectGetWidth(bounds)*0.5f)
controlPoint1:CGPointMake(CGRectGetWidth(bounds)*0.4f,CGRectGetHeight(bounds)*0.8f)
controlPoint2:CGPointMake(0,CGRectGetWidth(bounds)*0.5f+CGRectGetHeight(bounds)*0.1f)];
[drop closePath];
Not entirely sure if this works since I couldn't test it right now. You might have to play with the controls points a bit. It could be that I made some error with the orientation.
I'm trying to draw a chevron shape inside my UIView subclass. The chevron appears, but the line cap style and line join styles that I'm applying aren't being reflected in the output.
- (UIBezierPath *)chevron:(CGRect)frame
{
UIBezierPath* bezierPath = [[UIBezierPath alloc]init];
[bezierPath setLineJoinStyle:kCGLineJoinRound];
[bezierPath setLineCapStyle:kCGLineCapRound];
[bezierPath moveToPoint:CGPointMake(CGRectGetMinX(frame), CGRectGetMaxY(frame))];
[bezierPath addLineToPoint:CGPointMake(CGRectGetMaxX(frame), CGRectGetMaxY(frame) * 0.5)];
[bezierPath addLineToPoint:CGPointMake(CGRectGetMinX(frame), CGRectGetMinY(frame))];
return bezierPath;
}
-(void)drawRect:(CGRect)rect{
[self.color setStroke];
UIBezierPath *chevronPath = [self chevron:rect];
[chevronPath setLineWidth:self.strokeWidth];
[chevronPath stroke];
}
According to Apple's docs, they say that "After configuring the geometry and attributes of a Bezier path, you draw the path in the current graphics context using the stroke and fill methods" but that isn't working here —-- I've tried moving the setLineJoinStyle and setLineCapStyle statements around (e.g., after adding LineToPoint, inside drawRect) and it seems like no matter how many times I call them it isn't working. Any ideas what's going wrong?
Your code is applying those styles, you just can't see them because your chevron is being drawn all the way to the edge of your view then getting clipped. To see the ends of your chevron, change your call to the chevron method to this,
UIBezierPath *chevronPath = [self chevron:CGRectInset(rect, 10, 10)];
Whether 10 points is enough of an inset will depend on how wide your line is, so you may need to increase that.
I want to achieve the shape shown in image using UIBezier Path, and too the shape is filled with blocks in image it shows one block is filled, how to achieve this.
I have tried the following code taken from here
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, 10)];
[path addQuadCurveToPoint:CGPointMake(200, 10) controlPoint:CGPointMake(100, 5)];
[path addLineToPoint:CGPointMake(200, 0)];
[path addLineToPoint:CGPointMake(0, 0)];
[path closePath];
Thanks.
It looks to me like both the outline and also each block has the same shape. What you would probably do is to make one shape for the outline, and stroke it, and one shape for each cell and fill it.
Creating the shape
Each shape could be created something like this (as I've previously explained in this answer). It's done by stroking one path (the orange arc) which is a simple arc from one angle to another to get another path (the dashed outline)
Before we can stroke the path we to create it. CGPath's work just like UIBezierPath but with a C API. First we move to the start point, then we add an arc around the center from the one angle to another angle.
CGMutablePathRef arc = CGPathCreateMutable();
CGPathMoveToPoint(arc, NULL,
startPoint.x, startPoint.y);
CGPathAddArc(arc, NULL,
centerPoint.x, centerPoint.y,
radius,
startAngle,
endAngle,
YES);
Now that we have the centered arc, we can create one shape path by stroking it with a certain width. The resulting path is going to have the two straight lines (because we specify the "butt" line cap style) and the two arcs (inner and outer). As you saw in the image above, the stroke happens from the center an equal distance inwards and outwards.
CGFloat lineWidth = 10.0;
CGPathRef strokedArc =
CGPathCreateCopyByStrokingPath(arc, NULL,
lineWidth,
kCGLineCapButt,
kCGLineJoinMiter, // the default
10); // 10 is default miter limit
You would do this a couple of times to create one path for the stroked outline and one path for each cell.
Drawing the shape
Depending on if it's the outline or a cell you would either stroke it or fill it. You can either do this with Core Graphics inside drawRect: or with Core Animation using CAShapeLayers. Choose one and don't between them :)
Core Graphics
When using Core Graphics (inside drawRect:) you get the graphics context, configure the colors on it and then stroke the path. For example, the outline with a gray fill color and a black stroke color would look like this:
I know that your shape is filled white (or maybe it's clear) with a light blue stroke but I already had a gray and black image and I didn't want to create a new one ;)
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextAddPath(c, strokedArc); // the path we created above
CGContextSetFillColorWithColor(c, [UIColor lightGrayColor].CGColor);
CGContextSetStrokeColorWithColor(c, [UIColor blackColor].CGColor);
CGContextDrawPath(c, kCGPathFillStroke); // both fill and stroke
That will put something like this on screen
Core Animation
The same drawing could be done with a shape layer like this:
CAShapeLayer *outline = [CAShapeLayer layer];
outline.fillColor = [UIColor lightGrayColor].CGColor;
outline.strokeColor = [UIColor blackColor].CGColor;
outline.lineWidth = 1.0;
outline.path = strokedArc; // the path we created above
[self.view.layer addSublayer: outline];
I'm currently drawing on the screen. I get smooth lines, I can change the color of my drawings. But I can't find how to apply a shadow to that line.
To draw it, I use :
[path strokeWithBlendMode:[path blendMode] alpha:1.0];
I saw that I could use CGContextSetShadowWithColor() but even though, I'm not sure how to use it since here's what's said in the CGPath reference for strokeWithBlendMode:
This method automatically saves the current graphics state prior to
drawing and restores that state when it is done, so you do not have to
save the graphics state yourself.
So I don't really know where to put that CGContextSetShadowWithColor() or anything else if I can use it.
Regards
If you want to use CGContextSetShadowwithColor() then you will need to change the way to draw your bezierpath to the view so that you draw the CGPath representation to the CGContext. An example is below:
UIBezierPath *path; // this is your path as before
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddPath(context, path.CGPath);
CGContextSetLineWidth(context, 2.0);
CGContextSetBlendMode(context, path.blendMode);
CGContextSetShadowWithColor(context, CGSizeMake(1.0, 1.0), 2.0, [UIColor blackColor].CGColor);
CGContextStrokePath(context);
Another way you could do this is to create a new CAShapeLayer and draw you path to that by setting it as the path property. This will easily allow you to add a shadow that will only shadow your path.
In my custom control I have defined a few CGMutablePathRefs with the needed lines and arcs to draw my control; one draws the overall fill shape and others provide specular highlights. I have also defined two CGMutablePathRefs which contain the paths needed as clipping masks for the active and inactive state of the control.
What I'm struggling with is applying the clipping paths. I have previously used clipping paths for applying gradients to an image, but those drawing commands were of the CGContext... variety, not the CGPath... variety.
For testing purposes I have removed the specular highlight drawing aspects, just trying to get a large path clipped to a smaller path. This is what I had been testing with:
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextBeginPath(ctx);
CGContextAddPath(ctx, inactiveClip);
CGContextClosePath(ctx);
CGContextClip(ctx);
CGContextAddPath(ctx, frontFace);
CGContextSetLineWidth(ctx, 1.0);
CGContextSetFillColorWithColor(ctx, [[UIColor blackColor] CGColor]);
CGContextFillPath(ctx);
}
By putting the clipping command before any drawing, I thought I was saying to CoreGraphics, "Here's the region you should actually draw into."
Alas, nothing is drawn.
So assuming I had that ordering backwards I tried:
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextAddPath(ctx, frontFace);
CGContextSetLineWidth(ctx, 1.0);
CGContextSetFillColorWithColor(ctx, [[UIColor blackColor] CGColor]);
CGContextFillPath(ctx);
CGContextBeginPath(ctx);
CGContextAddPath(ctx, inactiveClip);
CGContextClosePath(ctx);
CGContextClip(ctx);
}
This was to say to CoreGraphics, "Okay before you actually color bits, check them against this clipping region."
Alas, nothing is clipped.
Since it is the case that my clipping path uses some of the same points and control points, in the same order, as the fill path, I have also replaced the call to CGContextClip with a call to CGContextEOClip to see if I was really struggling with the even-odd rule, but that doesn't seem to have had any visual affect.
I don't really know if I needed to bracket the CGContextAddPath call with CGContextBeginPath/CGContextClosePath calls, but what I was trying to do was minimize the differences between Apple's example code and my code. In theirs they do their CGContext... drawing calls between begin/close calls so I was too.
What am I misunderstanding here?