I have made Circle With below code
//Color Declaration
UIColor *color = [UIColor colorWithRed:0 green:0.429 blue:0 alpha:1];
//Drawing Circle
CGRect circleRect = CGRectMake(20, 20, 170, 170);
UIBezierPath *circlePath = [UIBezierPath bezierPath];
[circlePath addArcWithCenter:CGPointMake(CGRectGetMidX(circleRect), CGRectGetMidY(circleRect))
radius:CGRectGetWidth(circleRect)/2 startAngle:0 * M_PI/180
endAngle:289 * M_PI/180
clockwise:YES];
[circlePath addLineToPoint:CGPointMake(CGRectGetMidX(circleRect), CGRectGetMidY(circleRect))];
[circlePath closePath];
[color setFill];
[circlePath fill];
I have Validated Touches Code with Area of Circle With the Below code,
- (BOOL)validatePoint:(CGPoint)myPoint
{
// calculate how far from centre we are with Pythagorean
// √ a2 + b2
CGFloat a = abs(myPoint.x - (self.view.bounds.size.width/2));
CGFloat b = abs(myPoint.y - (self.view.bounds.size.height/2));
CGFloat distanceFromCentre = sqrt(pow(a,2) + pow(b,2));
if((distanceFromCentre > self.minRadiusSize) && (distanceFromCentre < self.maxRadiusSize)){
return YES;
}else{
// not inside doughnut
return NO;
}
}
I have Validated the Circle with Above code.When above code is true then we can add touches to Circle.
Similarly I have to validate the sector (Portion) of the Circle.
My Requirement is, I have to validate the Sector of Circle. were I have to Detect the sector(Portion) of the drawn Circle.
I have Formula
[Area of Sector = ½ × (θ × π/180) × r2 (when θ is in degrees)].
If all you are trying to do is to check if the point is inside the circle sector then you should use the same path to hit test the point
BOOL isInsideSector = [circlePath containsPoint:myPoint];
Related
Is it possible to draw a path around the visible part of a UIBezierPath?
Here's an example of my problem
Here's what I'd like to accomplish
Here's what I got so far:
- (void)drawRect:(CGRect)rect {
CGFloat side = MIN(rect.size.width, rect.size.height);
CGPoint center = CGPointMake(rect.size.width / 2.0f, rect.size.height / 2.0f);
UIColor *yinYangColor = [UIColor whiteColor];
UIBezierPath *yinYangPath = [UIBezierPath bezierPath];
// Draw Yin&Yang part
[yinYangPath addArcWithCenter:CGPointMake(center.x, center.y - side / 4.0f) radius:side / 4.0f startAngle:M_PI_2 endAngle:-M_PI_2 clockwise:YES];
[yinYangPath addArcWithCenter:CGPointMake(center.x, center.y + side / 4.0f) radius:side / 4.0f startAngle:M_PI_2 endAngle:-M_PI_2 clockwise:NO];
[yinYangPath addArcWithCenter:CGPointMake(center.x, center.y) radius:side / 2.0f startAngle:-M_PI_2 endAngle:M_PI_2 clockwise:YES];
[yinYangPath closePath];
[yinYangColor setFill];
[yinYangPath fill];
// Add border
CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init];
borderLayer.path = yinYangPath.CGPath;
borderLayer.fillColor = [UIColor clearColor].CGColor;
borderLayer.strokeColor = [UIColor blackColor].CGColor;
borderLayer.lineWidth = 5.0f;
[self.layer addSublayer:borderLayer];
}
The angle π/2 radians is along the positive y axis.
In standard UIKit geometry, the positive y axis points down toward the bottom of the screen. Therefore the top arc (at center.y - side/4) needs to start at angle -π/2 and end at angle π/2. Since you got these backward, your second arc doesn't start where your first arc ended, so your path contains a straight line connecting those points. Ditto for your second and third arcs. The single straight line visible in your image is actually the combination of those two lines.
Also, incidentally, the rect passed to drawRect: is in theory not necessarily the bounds of the view. It's better not to treat it as such.
Also also, you shouldn't add sublayers in drawRect:. You should do that in init or layoutSubviews and you should make sure you don't duplicate layers. I guess maybe you're using a CAShapeLayer because you don't want the border cut off. I would solve that by insetting the view bounds by the border width:
- (void)drawRect:(CGRect)dirtyRect {
CGFloat lineWidth = 4;
CGRect rect = CGRectInset(self.bounds, lineWidth / 2, lineWidth / 2);
CGFloat side = MIN(rect.size.width, rect.size.height);
CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
UIBezierPath *path = [UIBezierPath bezierPath];
CGFloat smallRadius = side / 4;
[path addArcWithCenter:CGPointMake(center.x, center.y - smallRadius) radius:smallRadius startAngle:-M_PI_2 endAngle:M_PI_2 clockwise:NO];
[path addArcWithCenter:CGPointMake(center.x, center.y + smallRadius) radius:smallRadius startAngle:-M_PI_2 endAngle:M_PI_2 clockwise:YES];
[path addArcWithCenter:center radius:side / 2 startAngle:M_PI_2 endAngle:-M_PI_2 clockwise:NO];
[path closePath];
[path setLineJoinStyle:kCGLineJoinRound];
[path setLineWidth:lineWidth];
[[UIColor whiteColor] setFill];
[path fill];
[[UIColor blackColor] setStroke];
[path stroke];
}
Result:
If you want the bottom tip to be more pointy, I would do that by clipping all drawing to the path, then drawing the border twice as thick. Half the border will be drawn outside the path and clipped away, leaving a sharp point. In this case, you don't have to inset the bounds.
- (void)drawRect:(CGRect)dirtyRect {
CGFloat lineWidth = 4 * 2;
CGRect rect = self.bounds;
CGFloat side = MIN(rect.size.width, rect.size.height);
CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
UIBezierPath *path = [UIBezierPath bezierPath];
CGFloat smallRadius = side / 4;
[path addArcWithCenter:CGPointMake(center.x, center.y - smallRadius) radius:smallRadius startAngle:-M_PI_2 endAngle:M_PI_2 clockwise:NO];
[path addArcWithCenter:CGPointMake(center.x, center.y + smallRadius) radius:smallRadius startAngle:-M_PI_2 endAngle:M_PI_2 clockwise:YES];
[path addArcWithCenter:center radius:side / 2 startAngle:M_PI_2 endAngle:-M_PI_2 clockwise:NO];
[path closePath];
[path setLineJoinStyle:kCGLineJoinRound];
[path addClip];
[path setLineWidth:lineWidth];
[[UIColor whiteColor] setFill];
[path fill];
[[UIColor blackColor] setStroke];
[path stroke];
}
Result:
I'm trying to draw a somewhat similar image to this, using Core Graphics:
I was able to draw the main arc, but I am not able to understand, how to divide arc into parts/draw graduation on arc? My current code to draw the arc is:
[path addArcWithCenter:point radius:radius startAngle:DEGREES_TO_RADIANS(specific_start_angle) endAngle:DEGREES_TO_RADIANS(specific_end_angle) clockwise:NO];
I tried using strokeWithBlendMode but I am facing problem with position of graduations or ticks.
Teja's solution will work fine for you, but it does require that you calculate your own start and end points for the graduations.
I suggest you create a function that let's you draw the graduations at a given angle of the arc, that will calculate the start and end points of the graduations, given a length.
static inline void drawGraduation(CGPoint center, CGFloat radius, CGFloat angle, CGFloat length, CGFloat width, CGColorRef color) {
CGContextRef c = UIGraphicsGetCurrentContext();
CGFloat radius2 = radius+length; // The radius of the end points of the graduations
CGPoint p1 = (CGPoint){cos(angle)*radius+center.x, sin(angle)*radius+center.y}; // the start point of the graduation
CGPoint p2 = (CGPoint){cos(angle)*radius2+center.x, sin(angle)*radius2+center.y}; // the end point of the graduation
CGContextMoveToPoint(c, p1.x, p1.y);
CGContextAddLineToPoint(c, p2.x, p2.y);
CGContextSetStrokeColorWithColor(c, color);
CGContextSetLineWidth(c, width);
CGContextStrokePath(c);
}
You can then call this in a for loop (or however you want to do it) when you draw your arc for the main scale of your VU meter. You can also easily customise the color, width and length of given graduations at given intervals (for example, this code gives a thicker & longer red line every 5 graduations).
- (void)drawRect:(CGRect)rect {
CGRect r = self.bounds;
CGFloat startAngle = -M_PI*0.2; // start angle of the main arc
CGFloat endAngle = -M_PI*0.8; // end angle of the main arc
NSUInteger numberOfGraduations = 16;
CGPoint center = (CGPoint){r.size.width*0.5, r.size.height*0.5}; // center of arc
CGFloat radius = (r.size.width*0.5)-20; // radius of arc
CGFloat maxGraduationWidth = 1.5; // the maximum graduation width
CGFloat maxGraduationWidthAngle = maxGraduationWidth/radius; // the maximum graduation width angle (used to prevent the graduations from being stroked outside of the main arc)
// draw graduations
CGFloat deltaArc = (endAngle-startAngle+maxGraduationWidthAngle)/(numberOfGraduations-1); // the change in angle of the arc
CGFloat startArc = startAngle-(maxGraduationWidthAngle*0.5); // the starting angle of the arc
for (int i = 0; i < numberOfGraduations; i++) {
if (i % 5 == 0) {
drawGraduation(center, radius, startArc+(i*deltaArc), 14, 1.5, [UIColor redColor].CGColor); // red graduation every 5 graduations.
} else {
drawGraduation(center, radius, startArc+(i*deltaArc), 10, 1, [UIColor blackColor].CGColor);
}
}
// draw main arc
UIBezierPath* mainArc = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:NO];
[[UIColor blackColor] setStroke];
mainArc.lineWidth = 2;
[mainArc stroke];
}
Output
Full project: https://github.com/hamishknight/VU-Meter-Arc
You can draw bezier path with dashes ,something like this::
//// Bezier Drawing
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(54.5, 62.5)];
[bezierPath addCurveToPoint: CGPointMake(121.5, 39.5) controlPoint1: CGPointMake(54.5, 62.5) controlPoint2: CGPointMake(87.5, 39.5)];
[bezierPath addCurveToPoint: CGPointMake(190.5, 62.5) controlPoint1: CGPointMake(155.5, 39.5) controlPoint2: CGPointMake(190.5, 62.5)];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 10;
CGFloat bezierPattern[] = {24, 2};
[bezierPath setLineDash: bezierPattern count: 2 phase: 0];
[bezierPath stroke];
Else You can draw multiple bezier path and draw it similar to what you want:
Something like this:
For the small line:
UIBezierPath* bezier2Path = [UIBezierPath bezierPath];
[bezier2Path moveToPoint: CGPointMake(65, 45)];
[bezier2Path addLineToPoint: CGPointMake(75, 63)];
[UIColor.blackColor setStroke];
bezier2Path.lineWidth = 1.5;
[bezier2Path stroke];
I'm trying to build a design app and one of the features allows people to preview different types of custom borders on various assets.
I am trying to get these borders to draw using UIBezierPath.
Right now the system will support:
various border radii (anything from 0 -> a ceiling calculated elsewhere )
different colors for the entire border
I'm trying to implement:
The ability to have different border line widths.
I think I should be able to achieve this by drawing each path with a different width, but I can't figure out how to change it from path to path. Can anyone help me out?
Thanks.
Click top see the border script in action
+(void)setBorderOnView :(id)object withWidth:(float)width andBorders:(NSArray*)borders ofColor:(UIColor*)color andRadius:(float)radius andRadii:(NSArray*)radii
{
UIView *objectView = object;
// Add half a pixel to compensate for border stroke, which puts .5 pixels outside of view.
float borderWidth = width+0.5;
float topLeftRadius = radius;
float topRightRadius = radius;
float bottomRightRadius = radius;
float bottomLeftRadius = radius;
CAShapeLayer *roundedCornerLayer = [CAShapeLayer layer];
CAShapeLayer *cornerMaskLayer = [CAShapeLayer layer];
UIColor * borderColor = color;
roundedCornerLayer.strokeColor = borderColor.CGColor;
// Begin corner path
UIBezierPath *roundedCorners = [UIBezierPath bezierPath];
[roundedCorners moveToPoint:CGPointMake(0, objectView.frame.size.height - bottomLeftRadius)];
[roundedCorners addLineToPoint:CGPointMake(0, 0 + topLeftRadius)];
if(topLeftRadius != 0){
[roundedCorners addArcWithCenter:CGPointMake(topLeftRadius, topLeftRadius)
radius:radius
startAngle:DEGREES_TO_RADIANS(180)
endAngle:DEGREES_TO_RADIANS(270)
clockwise:YES];
}
[roundedCorners addLineToPoint:CGPointMake(objectView.frame.size.width - topRightRadius, 0)];
if(topRightRadius != 0){
[roundedCorners addArcWithCenter:CGPointMake(objectView.frame.size.width - topRightRadius, 0 + topRightRadius)
radius:radius
startAngle:DEGREES_TO_RADIANS(270)
endAngle:DEGREES_TO_RADIANS(360)
clockwise:YES];
}
[roundedCorners addLineToPoint:CGPointMake(objectView.frame.size.width, objectView.frame.size.height - bottomRightRadius)];
if(bottomRightRadius != 0){
[roundedCorners addArcWithCenter:CGPointMake(objectView.frame.size.width - bottomRightRadius, objectView.frame.size.height - bottomRightRadius)
radius:radius
startAngle:DEGREES_TO_RADIANS(0)
endAngle:DEGREES_TO_RADIANS(90)
clockwise:YES];
}
[roundedCorners addLineToPoint:CGPointMake(0 + bottomLeftRadius, objectView.frame.size.height)];
if(bottomLeftRadius != 0){
[roundedCorners addArcWithCenter:CGPointMake(0 + bottomLeftRadius, objectView.frame.size.height - bottomLeftRadius)
radius:radius
startAngle:DEGREES_TO_RADIANS(90)
endAngle:DEGREES_TO_RADIANS(180)
clockwise:YES];
}
roundedCornerLayer.path = roundedCorners.CGPath;
roundedCornerLayer.fillColor = [UIColor clearColor].CGColor;
roundedCornerLayer.lineWidth = borderWidth;
[cornerMaskLayer setPath:roundedCorners.CGPath];
objectView.layer.mask = cornerMaskLayer;
[objectView.layer addSublayer:roundedCornerLayer];
}
roundedCornerLayer.lineWidth = borderWidth; sets the width of the path. I'm not sure if you want the same path to have different widths or different paths with different widths.
If you want different widths in one path then you must break it into separate subpaths and each will have a different lineWidth.
If you want to have different paths then just change the value of width when you call the method.
Did I miss anything?
Hiiii
I'm trying to draw in a view an arc with dynamic width.
I have built an ARRAY with several UIBezierPath with arcs then I draw one after the other. this is the code:
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGFloat radious;
if (self.bounds.size.width < self.bounds.size.height) {
radious = self.bounds.size.width / 2;
}
else{
radious = self.bounds.size.height / 2;
}
float oneGradeInRadians = (float)(2 * M_PI) / 360.f;
float radiandToDraw = (float)self.finalAngleRadians - self.initialAngleRadians;
float splitMeasure = oneGradeInRadians;
int numberOfArcs = radiandToDraw / splitMeasure;
NSMutableArray *arrayOfBeziersPaths = [NSMutableArray arrayWithCapacity:numberOfArcs];
for (int i = 0; i < numberOfArcs; i++) {
float startAngle = self.initialAngleRadians + (i * splitMeasure);
float endAngle = self.initialAngleRadians +((i + 1) * splitMeasure);
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width/2, self.frame.size.height/2) radius:radious - self.widthLine.floatValue startAngle:startAngle endAngle:endAngle clockwise:YES];
bezierPath.lineWidth = self.widthLine.floatValue + i/20;
float hue = (float)(i / 3.f);
UIColor *color = [UIColor colorWithHue:hue/360.0 saturation:1 brightness:1 alpha:1];
[color setStroke];
[arrayOfBeziersPaths addObject:bezierPath];
}
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineJoin(context, kCGLineJoinMiter);
//We saved the context
CGContextSaveGState(context);
for (int i = 0; i < numberOfArcs; i++) {
[(UIBezierPath *)[arrayOfBeziersPaths objectAtIndex:i] stroke];
[(UIBezierPath *)[arrayOfBeziersPaths objectAtIndex:i] fill];
}
//Restore the contex
CGContextRestoreGState(context);
}
In this way I get the right shape but, also I get an annoying lines between the arcs:
I think the problem may be a property to change between arcs, but I'm totally lost, or maybe there is another better way to build this.
I tried creating several UIBezierPath paths and I added them to an unique UIBezierPath, then I stoke and fill that path. I didn't get the annoying lines, but the problem is I can not modify the line width, so I can not get the same effect.
Any idea? thanks
dont create many such paths. just create two arcs (inner circle, exterior circle) and two straight lines joining the ends. then fill the path.
Add below code in a viewDidLoad of an empty viewController and check.
- (void)viewDidLoad
{
[super viewDidLoad];
CGFloat k =0.5522847498;
UIBezierPath *path =[UIBezierPath bezierPath];
CGFloat radius=130;
[path addArcWithCenter:CGPointMake(0, 0) radius:radius startAngle:M_PI_2 endAngle:M_PI clockwise:NO];
CGFloat start=10;
CGFloat increment=5;
[path addLineToPoint:CGPointMake(-radius-start, 0)];
[path addCurveToPoint:CGPointMake(0, -radius-start-increment) controlPoint1:CGPointMake(-radius-start, -(radius +start)*k) controlPoint2:CGPointMake(-(radius+start)*k, -radius-start-increment)];
[path addCurveToPoint:CGPointMake(radius+start+2*increment, 0) controlPoint1:CGPointMake((radius+start+increment)*k, -radius-start-increment) controlPoint2:CGPointMake(radius+start+2*increment, (-radius-start-increment)*k)];
[path addCurveToPoint:CGPointMake(0, radius+start+3*increment) controlPoint1:CGPointMake(radius+start+2*increment, (radius+start+2*increment)*k) controlPoint2:CGPointMake((radius+start+2*increment)*k,radius+start+3*increment)];
[path addLineToPoint:CGPointMake(0,radius)];
CAShapeLayer *layer =[CAShapeLayer layer];
[layer setFrame:CGRectMake(150, 200, 300, 300)];
[layer setFillColor:[UIColor greenColor].CGColor];
[layer setStrokeColor:[UIColor blackColor].CGColor];
[layer setPath:path.CGPath];
[self.view.layer addSublayer:layer];
}
It produced result like,
The problem here is that you’ve chosen a crazy approach to drawing the shape you’re after. If you take a look at it, you’ll see that it’s actually a filled region bounded on the outside by a circular arc, and on the inner edge by a spiral.
What you should do, therefore, is create a single NSBezierPath, add the outer circular arc to it, then add a line to the start of your spiral, append a spiral (you’ll want to approximate it with Bézier segments) and finally call -closePath. After that, you can -fill the path and you’ll get a good looking result rather than the mess you have above.
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:.