UIBezierPath bezierPathWithArcCenter with degree lines - ios

i am creating a circle with
+(UIBezierPath *)circleShape:(CGRect)originalFrame{
CGRect frame = [self maximumSquareFrameThatFits:originalFrame];
UIBezierPath *aPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(160, 150)
radius:100
startAngle:0
endAngle:DEGREES_TO_RADIANS(360)
clockwise:YES];
return aPath;
}
I want to add 2 lines vertical and horizontal through the center and extend past the bounds of the clircle
I have tried addLineToPoint but i get nothing in the circle
TIA
Thanks, im adding lines like so and apart from playing with positioning the line extending past the bounds of the circle are displaying but im guessing the lines in the circle are the same color as the circle, so not visible. Do i need to create the lines in another method then use appendPath? so i can assign a different color.
UIBezierPath *aPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(160, 150)
radius:100
startAngle:0
endAngle:DEGREES_TO_RADIANS(360)
clockwise:YES];
[aPath moveToPoint: CGPointMake(CGRectGetMinX(frame)
+ 0.30000 * CGRectGetWidth(frame),CGRectGetMinY(frame) + 0.15000
* CGRectGetHeight(frame))];
[aPath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.27634
* CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.10729 * CGRectGetHeight(frame))];
[aPath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.97553 *
CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.39549 * CGRectGetHeight(frame))];
[aPath closePath];
return aPath;

After you draw the circle, the current graphics location is the last point on the circle. If you call addLineToPoint, the line is drawn from the last point on the circle to the end point that you pass to addLineToPoint.
To start a line that's not attached to the endpoint of the circle, use moveToPoint to change the current graphics location.
Note that if you stroke the path and then fill it, then the lines inside the circle will not be shown. That's because the fill operation will overwrite any pixels that were drawn inside the circle. If you fill the path first, and then stroke it, then the lines will be drawn on top of the circle (provided that the stroke and fill colors are different).

Related

Fill UIBezierPath with parallel lines

I'm trying to draw custom shape using UIBezierPath:
UIBezierPath *aPath = [UIBezierPath bezierPath];
[aPath moveToPoint:CGPointMake(100.0, 0.0)];
// Draw the lines.
[aPath addLineToPoint:CGPointMake(200.0, 40.0)];
[aPath addLineToPoint:CGPointMake(160, 140)];
[aPath addLineToPoint:CGPointMake(40.0, 140)];
[aPath addLineToPoint:CGPointMake(0.0, 40.0)];
[aPath closePath];
I want to fill it with parallel lines to make this stripped. I want to change color of this lines too.
Assume that I want to make them vertical.
I have to calculate somehow points on this path with regular interval, how I can do this?
I have found this UIColor colorWithPatternImage but then i cant change color and "density" of my lines inside shape.
Like Nikolai Ruhe said, the best option is to use your shape as the clipping path, and then draw some pattern inside the bounding box of the shape. Here's an example of what the code would look like
- (void)drawRect:(CGRect)rect
{
// create a UIBezierPath for the outline shape
UIBezierPath *aPath = [UIBezierPath bezierPath];
[aPath moveToPoint:CGPointMake(100.0, 0.0)];
[aPath addLineToPoint:CGPointMake(200.0, 40.0)];
[aPath addLineToPoint:CGPointMake(160, 140)];
[aPath addLineToPoint:CGPointMake(40.0, 140)];
[aPath addLineToPoint:CGPointMake(0.0, 40.0)];
[aPath closePath];
[aPath setLineWidth:10];
// get the bounding rectangle for the outline shape
CGRect bounds = aPath.bounds;
// create a UIBezierPath for the fill pattern
UIBezierPath *stripes = [UIBezierPath bezierPath];
for ( int x = 0; x < bounds.size.width; x += 20 )
{
[stripes moveToPoint:CGPointMake( bounds.origin.x + x, bounds.origin.y )];
[stripes addLineToPoint:CGPointMake( bounds.origin.x + x, bounds.origin.y + bounds.size.height )];
}
[stripes setLineWidth:10];
CGContextRef context = UIGraphicsGetCurrentContext();
// draw the fill pattern first, using the outline to clip
CGContextSaveGState( context ); // save the graphics state
[aPath addClip]; // use the outline as the clipping path
[[UIColor blueColor] set]; // blue color for vertical stripes
[stripes stroke]; // draw the stripes
CGContextRestoreGState( context ); // restore the graphics state, removes the clipping path
// draw the outline of the shape
[[UIColor greenColor] set]; // green color for the outline
[aPath stroke]; // draw the outline
}
Using Swift
override func draw(_ rect: CGRect){
// create a UIBezierPath for the outline shape
let aPath = UIBezierPath()
aPath.move(to: CGPoint(x: 100.0, y: 0.0))
aPath.addLine(to: CGPoint(x: 200.0, y: 40.0))
aPath.addLine(to: CGPoint(x: 160, y: 140))
aPath.addLine(to: CGPoint(x: 40.0, y: 140))
aPath.addLine(to: CGPoint(x: 0.0, y: 40.0))
aPath.close()
aPath.lineWidth = 10
// get the bounding rectangle for the outline shape
let bounds = aPath.bounds
// create a UIBezierPath for the fill pattern
let stripes = UIBezierPath()
for x in stride(from: 0, to: bounds.size.width, by: 20){
stripes.move(to: CGPoint(x: bounds.origin.x + x, y: bounds.origin.y ))
stripes.addLine(to: CGPoint(x: bounds.origin.x + x, y: bounds.origin.y + bounds.size.height ))
}
stripes.lineWidth = 10
let context = UIGraphicsGetCurrentContext()
// draw the fill pattern first, using the outline to clip
context!.saveGState() // save the graphics state
aPath.addClip() // use the outline as the clipping path
UIColor.blue.set() // blue color for vertical stripes
stripes.stroke() // draw the stripes
context!.restoreGState() // restore the graphics state, removes the clipping path
// draw the outline of the shape
UIColor.green.set() // green color for the outline
aPath.stroke() // draw the outline
}
Produces
The best option is to set the original shape that you want to draw as a clipping path (CGContextClip) on the context.
Now, just draw all the parallel lines into the bounding box of the shape. You are free to vary the color or distance of the lines, then.
I don't have code for this at the moment but you are going down the right path with [UIColor colorWithPatternImage:....
However, if you want to make this more flexible you could generate the image programatically.
You only need a very small image of a few pixels. Enough for one of each colour of stripe.
If your stripes are green and red and the green is 4 pixels wide and the red 3 pixels wide then you only need an image that is 1 pixel wide and 7 pixels tall.
Then use this automatically generated image as the pattern image for the colour.
That's probably the easiest way I can think of.
Generating an image is not very hard. There are many SO questions about drawing into an image context.

how to draw an arc with line using UIBezierPath

I am trying to draw shape shown in figure. Background is white.. Hope it is visible to you..
I am using bezier path to draw this. I have provided bounds to shape as shown by blue border.
So far I am successfully able to draw just two lines(shown in green). I have to draw the one with red further.
I am unable to draw arc from this point. I can't understand how to pass correct parameters to addArcWithCenter.
Code
-(void) drawRect:(CGRect)rect
{
//declare and instantiate the UIBezierPath object
aPath = [UIBezierPath bezierPath];
// Set the starting point of the shape.
[aPath moveToPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect))];
// Draw some lines.
[aPath addLineToPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect))];
[aPath addLineToPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect) - 40)];
[aPath addArcWithCenter:self.center radius:40 startAngle:3 *(M_PI / 2) endAngle:M_PI clockwise:NO];
//set the line width
aPath.lineWidth = 2;
//set the stoke color
[[UIColor greenColor] setStroke];
//draw the path
[aPath stroke];
}
I am new to core graphics. Please be lenient over me.. Thanks..
Try with this (as you can see, I've used addQuadCurveToPoint a variant of addCurveToPoint proposed by #wain - ask google for addCurveToPoint and switch to picture search to see how it work ) :
-(void) drawRect:(CGRect)rect
{
UIBezierPath * aPath = [UIBezierPath bezierPath];
// Set the starting point of the shape.
[aPath moveToPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect))];
// Draw some lines.
[aPath addLineToPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect))];
//changes start here !
//the point look to be at 80% down
[aPath addLineToPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect) * .8)];
//1st arc
//The end point look to be at 1/4 at left, bottom
CGPoint p = CGPointMake(CGRectGetMaxX(rect) / 4, CGRectGetMaxY(rect));
CGPoint cp = CGPointMake( (CGRectGetMaxX(rect) / 4) + ((CGRectGetMaxX(rect) - (CGRectGetMaxX(rect) / 4)) / 2) , CGRectGetMaxY(rect) * .8);
[aPath addQuadCurveToPoint:p controlPoint:cp];
//2nd arc
//The end point look to be at 80% downt at left,
CGPoint p2 = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect) * .8);
CGPoint cp2 = CGPointMake( (CGRectGetMaxX(rect) / 4) / 2 , CGRectGetMaxY(rect) * .8);
[aPath addQuadCurveToPoint:p2 controlPoint:cp2];
//close the path
[aPath closePath];
//set the line width
aPath.lineWidth = 2;
//set the stoke color
[[UIColor greenColor] setStroke];
//draw the path
[aPath stroke];
}

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

UIBezierPath and applytransform

I am trying to implement a custom UIView which is basically a pie menu (something like a cake divided into slices).
For that I am trying to draw a circle and a series of lines from the center, like the rays in a chart's wheel.
I have successfully drawn the circle and and I would now like to draw the lines dividing the circle in slices.
This is what I have so far:
-(void)drawRect:(CGRect)rect{
[[UIColor blackColor] setStroke];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGFloat minDim = (rect.size.width < rect.size.height) ? rect.size.width : rect.size.height;
CGRect circleRect = CGRectMake(0, rect.size.height/2-minDim/2, minDim, minDim);
CGContextAddEllipseInRect(ctx, circleRect);
CGContextSetFillColor(ctx, CGColorGetComponents([[UIColor yellowColor] CGColor]));
CGContextFillPath(ctx);
CGPoint start = CGPointMake(0, rect.size.height/2);
CGPoint end = CGPointMake(rect.size.width, rect.size.height/2);
for (int i = 0; i < MaxSlices(6); i++){
CGFloat degrees = 1.0*i*(180/MaxSlices(6));
CGAffineTransform rot = CGAffineTransformMakeRotation(degreesToRadians(degrees));
UIBezierPath *path = [self pathFrom:start to:end];
[path applyTransform:rot];
}
}
- (UIBezierPath *) pathFrom:(CGPoint) start to:(CGPoint) end{
UIBezierPath* aPath = [UIBezierPath bezierPath];
aPath.lineWidth = 5;
[aPath moveToPoint:start];
[aPath addLineToPoint:end];
[aPath closePath];
[aPath stroke];
return aPath;
}
The problem is that applyTransform on the path doesn't seem to do anything. The first path gest drawn correctly the and the following ones are unaffected by the rotation. Basically what I see is just one path. Check the screen shot here http://img837.imageshack.us/img837/9757/iossimulatorscreenshotf.png
Thank you for your help!
You're drawing the path (with stroke) before you transform it. A path is just a mathematical representation. It isn't the line "on the screen." You can't move what you've already drawn by modifying the data about it.
Just move the [aPath stroke] out of pathFrom:to: and put it after the applyTransform:.

iOS Animate Dashed Rectangle Border

I have a UIImageView displaying an image. I want to "highlight" a portion of the image by drawing a rounded rectangle outline. I would like to have the outline drawn with a thick, dashed line that "animates" by continually varying where the "beginning" of the line starts.
I thought about drawing a circle that had the look I want and then simply animating it, but I really need a rectangular solution, so that's out.
Background:
I'm drawing the rounded rectangle border by calculating 8 points and drawing 4 straight lines and 4 curves. (Maybe this can be easier, but it's not the broken part!)
My thinking is that I'll use an "offset" variable that starts at the top-left of the rounded rectangle, where the top-left curve meets the top straight piece. Then, I will increment this "offset" across the top of the rounded rectangle until it reaches the top-right curve, whereupon I will "reset" the "offset" variable to its original value.
This is working pretty much as I'd like, until the "reset" occurs. At this point, the animation is jerky (kind of expected), but it also appears to travel in reverse for a small portion of the time, before resuming "forward" motion. Finally, at the beginning/end of my dashed line, I get an extra long segment on the dashed line. I know they can't all be equal-length (can they? how to calculate?), but how can I make 2 shorter segments rather than 1 longer segment?
Anybody have an idea of what I can do to get a smooth "marching ants" kind of look? Any other ideas on a good way to (using animation) call the user's eye to a particular area of the screen? (It needs to surround a particular area without obscuring it.)
Current code:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
// Rounded corner will be 10% of average side length (i.e., (w + h) / 2)
float averageSide = ([self HighlightRect].size.width + [self HighlightRect].size.height) / 2.0;
float roundSize = averageSide * 0.10;
// offset is a static, class variable
offset += roundSize / 4.0;
if ([WhereIAmView offset] < roundSize) {
offset = roundSize;
}
if ([WhereIAmView offset] > ([self HighlightRect].size.width - roundSize)) {
offset = roundSize;
}
// Set the "main" color of the rounded rectangle
UIColor *lineColor = [UIColor colorWithRed:027.0/255.0 green:050.0/255.0 blue:224.0/255.0 alpha:1.0];
CGContextSetStrokeColorWithColor(context, [lineColor CGColor]);
CGContextSetLineWidth(context, 16.0);
CGFloat pattern[] = {25.0, 5.0};
CGContextSetLineDash(context, offset, pattern, 2);
CGRect rRect = [self HighlightRect];
// The top left corner
CGPoint topLeft = CGPointMake(rRect.origin.x, rRect.origin.y);
// The top right corner
CGPoint topRight = CGPointMake(rRect.origin.x + rRect.size.width, rRect.origin.y);
// The bottom right corner
CGPoint bottomRight = CGPointMake(rRect.origin.x + rRect.size.width, rRect.origin.y + rRect.size.height);
// The bottom left corner
CGPoint bottomLeft = CGPointMake(rRect.origin.x, rRect.origin.y + rRect.size.height);
// The two points across the top of the rounded rectangle (left to right)
CGPoint point1 = CGPointMake(rRect.origin.x + roundSize, rRect.origin.y);
CGPoint point2 = CGPointMake(rRect.origin.x + rRect.size.width - roundSize, rRect.origin.y);
// The two points along the right of the rounded rectangle (top to bottom)
CGPoint point3 = CGPointMake(rRect.origin.x + rRect.size.width, rRect.origin.y + roundSize);
CGPoint point4 = CGPointMake(rRect.origin.x + rRect.size.width, rRect.origin.y + rRect.size.height - roundSize);
// The two points along the bottom of the rounded rectangle (right to left)
CGPoint point5 = CGPointMake(rRect.origin.x + rRect.size.width - roundSize, rRect.origin.y + rRect.size.height);
CGPoint point6 = CGPointMake(rRect.origin.x + roundSize, rRect.origin.y + rRect.size.height);
// The two points along the left of the rounded rectangle (bottom to top)
CGPoint point7 = CGPointMake(rRect.origin.x, rRect.origin.y + rRect.size.height - roundSize);
CGPoint point8 = CGPointMake(rRect.origin.x, rRect.origin.y + roundSize);
// Move to point 1
CGContextMoveToPoint(context, point1.x, point1.y);
// Add line to point 2 (this is the straight portion across the top)
CGContextAddLineToPoint(context, point2.x, point2.y);
// Add curve to point 3 (this is the rounded portion in top right)
CGContextAddArcToPoint(context, topRight.x, topRight.y, point3.x, point3.y, roundSize);
// Add line to point 4 (this is the straight portion across the right)
CGContextAddLineToPoint(context, point4.x, point4.y);
// Add curve to point 5 (this is the rounded portion in bottom right)
CGContextAddArcToPoint(context, bottomRight.x, bottomRight.y, point5.x, point5.y, roundSize);
// Add line to point 6 (this is the straight portion across the bottom)
CGContextAddLineToPoint(context, point6.x, point6.y);
// Add curve to point 7 (this is the rounded portion in bottom left)
CGContextAddArcToPoint(context, bottomLeft.x, bottomLeft.y, point7.x, point7.y, roundSize);
// Add line to point 8 (this is the straight portion across the left)
CGContextAddLineToPoint(context, point8.x, point8.y);
// Add curve to point 1 (this is the rounded portion in top left)
CGContextAddArcToPoint(context, topLeft.x, topLeft.y, point1.x, point1.y, roundSize);
// Stroke the path
CGContextStrokePath(context);
}
bump
bump bump
Try Using CAShapeLayer with CGPath of your shape.
Rounded rectangle path can be constructed using Uibezierpath convenience method.
You can set a line pattern for the shape layer. Animating the line property of the shape layer would give the "marching ants like effect".
shapeLayer = [CAShapeLayer layer];
CGRect shapeRect = CGRectMake(0.0f, 0.0f, 200.0f, 100.0f);
[shapeLayer setBounds:shapeRect];
[shapeLayer setPosition:CGPointMake(160.0f, 140.0f)];
[shapeLayer setFillColor:[[UIColor clearColor] CGColor]];
[shapeLayer setStrokeColor:[[UIColor blackColor] CGColor]];
[shapeLayer setLineWidth:1.0f];
[shapeLayer setLineJoin:kCALineJoinRound];
[shapeLayer setLineDashPattern:
[NSArray arrayWithObjects:[NSNumber numberWithInt:10],
[NSNumber numberWithInt:5],
nil]];
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:shapeRect cornerRadius:15.0];
[shapeLayer setPath:path.CGPath];
[[imageview layer] addSublayer:shapeLayer];
The animation function can be,
- (void)toggleMarching
{
if ([shapeLayer animationForKey:#"linePhase"])
[shapeLayer removeAnimationForKey:#"linePhase"];
else {
CABasicAnimation *dashAnimation;
dashAnimation = [CABasicAnimation
animationWithKeyPath:#"lineDashPhase"];
[dashAnimation setFromValue:[NSNumber numberWithFloat:0.0f]];
[dashAnimation setToValue:[NSNumber numberWithFloat:15.0f]];
[dashAnimation setDuration:0.75f];
[dashAnimation setRepeatCount:10000];
[shapeLayer addAnimation:dashAnimation forKey:#"linePhase"];
}
}

Resources