Irregularly shaped UIBezierPath filling - iphone

I'm making some custom control. One of the subviews of the control is transparent UIView with UIBezierPath path drawn on. On the one of the photos there is just let's say border of the UIBezierPath (there's called [path stroke]), but on the second one you can see what happens when I call [path fill]. I'd like the path filled to create shape similar to the one on the first photo. drawRect method from this transparent UIView is below.
- (void)drawRect:(CGRect)rect
{
// Drawing code
[super drawRect:rect];
UIBezierPath *path;
[[UIColor greenColor] setStroke];
[[UIColor greenColor] setFill];
//Angles
CGFloat startAngle = degreesToRadians(225);
CGFloat endAngle = degreesToRadians(315);
CGPoint center = CGPointMake(CGRectGetMidX(rect), lRadius);
path = [UIBezierPath bezierPathWithArcCenter: center radius:lRadius startAngle:startAngle endAngle:endAngle clockwise:YES];
rightEndOfLine = path.currentPoint;
bigArcHeight = rightEndOfLine.y;
CGFloat smallArcHeight = lRadius - bigArcHeight - sRadius;
CGFloat smallArcWidthSquare = (8*smallArcHeight*sRadius) - (4 * (smallArcHeight * smallArcHeight));
smallArcWidth = sqrtf(smallArcWidthSquare);
leftStartOfLine = CGPointMake((rect.size.width - smallArcWidth)/2, lRadius-sRadius + smallArcHeight);
CGFloat lengthOfSpace = (rect.size.width - rightEndOfLine.x);
[path moveToPoint:leftStartOfLine];
[path addArcWithCenter:center radius:sRadius startAngle:startAngle endAngle:endAngle clockwise:YES];
rightStartOfLine = path.currentPoint;
leftEndOfLine = CGPointMake(lengthOfSpace, bigArcHeight);
[path moveToPoint:rightStartOfLine];
[path addLineToPoint:rightEndOfLine];
[path moveToPoint:leftStartOfLine];
[path addLineToPoint:leftEndOfLine];
[path closePath];
[path stroke];
// [path fill];
}
Thanks for help !

Try to remove all of your moveToPoint calls. This is a continuous shape so they are not required, and they are confusing the filling algorithm. You can start at the top left corner, draw the arc clockwise, draw the segment (?) line, draw the arc anticlockwise, then draw the last segment line (or close the path). This will fill properly.

Related

Draw a simple vertical bar in iphone

I wanted to draw a vertical single bar graph. I was trying to do it using DrawRect, but could not able to do so. Can nay one hlep me to knwo if this can be done easily by providing start and end point in view to change the color.
thanks
If you have a custom view, it's just a matter of drawing the lines and rectangles that you want. For example:
- (void)drawRect:(CGRect)rect
{
CGAffineTransform t = CGAffineTransformMakeScale(1, -1);
self.transform = t;
CGFloat baseline = 50;
CGFloat inset = 40;
CGFloat barWidth = 20;
CGFloat barHeight = 80;
CGRect r = CGRectMake(inset + barWidth, baseline, barWidth, barHeight);
UIBezierPath *p = [UIBezierPath bezierPathWithRect:r];
[[UIColor redColor] set];
[p fill];
p = [UIBezierPath bezierPath];
[p moveToPoint:CGPointMake(inset, baseline)];
[p addLineToPoint:CGPointMake(inset + 3 * barWidth, baseline)];
[[UIColor blackColor] set];
[p stroke];
}
produces this:
I've created a UIView subclass with the -drawRect: method above and created an instance of that view that's the size of the window. Note that I flipped the coordinate system using a transform -- you don't have to do that, but drawing with the origin at the lower left corner can be easier.
try this one
1. http://www.raywenderlich.com/13271/how-to-draw-graphs-with-core-plot-part-2
2 OVGraph.- a library for the graphs

Draw hollow circle divided into equal parts

I want draw a hollow circle that look like the below image
I got the below code snippet to draw hollow circle
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor whiteColor];
// Determine our start and stop angles for the arc (in radians)
startAngle = M_PI * 1.5;
endAngle = startAngle + (M_PI * 2);
}
return self;
}
- (void)drawRect:(CGRect)rect
{
// Display our percentage as a string
NSString* textContent = [NSString stringWithFormat:#"%d", self.percent];
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
// Create our arc, with the correct angles
[bezierPath addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2)
radius:130
startAngle:startAngle
endAngle:(endAngle - startAngle) * (_percent / 100.0) + startAngle
clockwise:YES];
// Set the display for the path, and stroke it
bezierPath.lineWidth = 50;
[[UIColor redColor] setStroke];
[bezierPath stroke];
// Text Drawing
CGRect textRect = CGRectMake((rect.size.width / 2.0) - 71/2.0, (rect.size.height / 2.0) - 45/2.0, 71, 45);
[[UIColor blackColor] setFill];
[textContent drawInRect: textRect withFont: [UIFont fontWithName: #"Helvetica-Bold" size: 42.5] lineBreakMode: NSLineBreakByWordWrapping alignment: NSTextAlignmentCenter];
}
I need the separator lines also in the hollow circle,as in the above image.Any suggestion will be helpful ....
Take a look at HKCircularProgressView.
I'm using it for my own project, extending the functionality for your need should be easy.
Not answering exactly what was asked, but a Simple solution for dividing any circle in three equal parts that solved my purpose-
- (void)drawRect:(CGRect)rect {
CGPoint center = CGPointMake(CGRectGetWidth(self.bounds) / 2.f, CGRectGetHeight(self.bounds) / 2.f);
CGFloat radius = 8.f;
UIBezierPath *portionPath1 = [UIBezierPath bezierPath];
[portionPath1 moveToPoint:center];
[portionPath1 addArcWithCenter:center radius:radius startAngle:radians(0) endAngle:radians(120) clockwise:YES];
[portionPath1 closePath];
[[UIColor greenColor] setFill];
[portionPath1 fill];
UIBezierPath *portionPath2 = [UIBezierPath bezierPath];
[portionPath2 moveToPoint:center];
[portionPath2 addArcWithCenter:center radius:radius startAngle:radians(120) endAngle:radians(240) clockwise:YES];
[portionPath2 closePath];
[[UIColor yellowColor] setFill];
[portionPath2 fill];
UIBezierPath *portionPath3 = [UIBezierPath bezierPath];
[portionPath3 moveToPoint:center];
[portionPath3 addArcWithCenter:center radius:radius startAngle:radians(240) endAngle:radians(360) clockwise:YES];
[portionPath3 closePath];
[[UIColor blueColor] setFill];
[portionPath3 fill]; }
At top put these two lines.
#define PI 3.14159265358979323846
static inline float radians(double degrees) { return degrees * PI / 180; }
Hope this will help anybody else too. :)

Drawing animation

I'm creating a simple app where when the user presses a button, a series of lines will be drawn on the screen and the user will be able to see these lines drawn in real time (almost like an animation).
My code looks something like this (has been simplified):
UIGraphicsBeginImageContext(CGSizeMake(300,300));
CGContextRef context = UIGraphicsGetCurrentContext();
for (int i = 0; i < 100; i++ ) {
CGContextMoveToPoint(context, i, i);
CGContextAddLineToPoint(context, i+20, i+20);
CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
CGContextStrokePath(context);
}
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
My problem is that:
1) As soon as the user presses the button, the UIThread blocks until the drawing is done.
2) I can't get the lines to be drawn on the screen one at a time - I've tried setting the UIImage directly inside the loop and also tried setting a layer content inside the loop.
How do I get around these problems?
You say "just like an animation". Why not do an actual animation, a la Core Graphics' CABasicAnimation? Do you really need to show it as a series of lines, or is a proper animation ok?
If you want to animate the actual drawing of the line, you could do something like:
#import <QuartzCore/QuartzCore.h>
- (void)drawBezierAnimate:(BOOL)animate
{
UIBezierPath *bezierPath = [self bezierPath];
CAShapeLayer *bezier = [[CAShapeLayer alloc] init];
bezier.path = bezierPath.CGPath;
bezier.strokeColor = [UIColor blueColor].CGColor;
bezier.fillColor = [UIColor clearColor].CGColor;
bezier.lineWidth = 5.0;
bezier.strokeStart = 0.0;
bezier.strokeEnd = 1.0;
[self.view.layer addSublayer:bezier];
if (animate)
{
CABasicAnimation *animateStrokeEnd = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
animateStrokeEnd.duration = 10.0;
animateStrokeEnd.fromValue = [NSNumber numberWithFloat:0.0f];
animateStrokeEnd.toValue = [NSNumber numberWithFloat:1.0f];
[bezier addAnimation:animateStrokeEnd forKey:#"strokeEndAnimation"];
}
}
Then all you have to do is create the UIBezierPath for your line, e.g.:
- (UIBezierPath *)bezierPath
{
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0.0, 0.0)];
[path addLineToPoint:CGPointMake(200.0, 200.0)];
return path;
}
If you want, you can patch a bunch of lines together into a single path, e.g. here is a roughly sine curve shaped series of lines:
- (UIBezierPath *)bezierPath
{
UIBezierPath *path = [UIBezierPath bezierPath];
CGPoint point = self.view.center;
[path moveToPoint:CGPointMake(0, self.view.frame.size.height / 2.0)];
for (CGFloat f = 0.0; f < M_PI * 2; f += 0.75)
{
point = CGPointMake(f / (M_PI * 2) * self.view.frame.size.width, sinf(f) * 200.0 + self.view.frame.size.height / 2.0);
[path addLineToPoint:point];
}
return path;
}
And these don't block the main thread.
By the way, you'll obviously have to add the CoreGraphics.framework to your target's Build Settings under Link Binary With Libraries.

Making the outer-edges of a UIBezierPath semi-transparent

I am currently drawing a UIImage within a UIBezierPath like so:
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
UIBezierPath *polygonPath = [UIBezierPath bezierPath];
CGPoint curPoint = [self originalPoint:[[drawArray objectAtIndex:0]CGPointValue]];
// set the starting point for the lines
[polygonPath moveToPoint:curPoint];
// go through each point in drawArray and add it to polygonPath
for(NSInteger i = 1; i < [drawArray count]; i++) {
curPoint = [self originalPoint:[[drawArray objectAtIndex:i]CGPointValue]];
// scale point
curPoint = CGPointMake(curPoint.x, curPoint.y);
[polygonPath addLineToPoint:curPoint];
}
[polygonPath closePath];
[polygonPath addClip];
[image drawAtPoint:CGPointZero];
It works great, however I'd like to have "smooth edges", slightly transparent, for the image, as I'm drawing it on top of another image... Right now the edges are pretty rough.
Thanks for your help!
As you are drawing on the image. So I suggest you to use following code...
Where YourImageis the UIImage on which you are drawing line.
[[UIColor colorWithPatternImage:YourImage] set];
For More Visit this reference
Hope, this will help you...

Custom UIButton Shape without using an image

I want to make a UIButton with that looks similar to the UIBackBarButtonItem (with an arrow pointing left for the navigation stack. I would prefer to do this without having to use an image if possible because the button will have a different size depending on the phone's orientation.
Is there a way to active this affect in code? My idea was using the CALayer of the button somehow.
Thanks!
EDIT
I am trying to use #Deepak's advice, but I am running into a a problem. I want the right side of the button to look like a [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:4] with the left side looking like an arrow. I tried to do this using the addQuadCurveToPoint:controlPoint method.
I am using the corner of the rect as the control point, but the path does not curve like I expect. It is still cornered as if I only used the addLineToPoint: method. My code is below.
float radius = 4.0;
UIBezierPath *path = [UIBezierPath bezierPath];
CGPoint startPoint = CGPointMake(rect.size.width/5.0, 0);
CGPoint pointBeforeTopCurve = CGPointMake(rect.size.width - radius, 0);
CGPoint topRightCorner = CGPointMake(rect.size.width, 0);
CGPoint pointAfterTopCurve = CGPointMake(rect.size.width, 0.0-radius);
CGPoint pointBeforeBottomCurve = CGPointMake(rect.size.width, rect.size.height-radius);
CGPoint bottomRightCorner = CGPointMake(rect.size.width, rect.size.height);
CGPoint pointAfterBottomCurve = CGPointMake(rect.size.width - radius, rect.size.height);
CGPoint pointBeforeArrow = CGPointMake(rect.size.width/5.0, rect.size.height);
CGPoint arrowPoint = CGPointMake(0, rect.size.height/2.0);
[path moveToPoint:pointBeforeTopCurve];
[path addQuadCurveToPoint:pointAfterTopCurve controlPoint:topRightCorner];
[path addLineToPoint:pointBeforeBottomCurve];
[path addQuadCurveToPoint:pointAfterBottomCurve controlPoint:bottomRightCorner];
[path addLineToPoint:pointBeforeArrow];
[path addLineToPoint:arrowPoint];
[path addLineToPoint:startPoint];
You can do this with Quartz. You will need subclass UIButton and implement its drawRect: method. You will have to define a path and fill it with a gradient.
You will also have to implement hitTest:withEvent: as it involves non-rectangular shape.
This solved the drawing difficulties for me
float radius = 10.0;
UIBezierPath *path = [UIBezierPath bezierPath];
CGPoint startPoint = CGPointMake(rect.size.width/5.0, 0);
CGPoint pointBeforeTopCurve = CGPointMake(rect.size.width - radius, 0);
CGPoint topRightCorner = CGPointMake(rect.size.width, 0.0);
CGPoint pointAfterTopCurve = CGPointMake(rect.size.width, radius);
CGPoint pointBeforeBottomCurve = CGPointMake(rect.size.width, rect.size.height-radius);
CGPoint bottomRightCorner = CGPointMake(rect.size.width, rect.size.height);
CGPoint pointAfterBottomCurve = CGPointMake(rect.size.width - radius, rect.size.height);
CGPoint pointBeforeArrow = CGPointMake(rect.size.width/5.0, rect.size.height);
CGPoint arrowPoint = CGPointMake(0.0, rect.size.height/2.0);
[path moveToPoint:pointBeforeTopCurve];
[path addQuadCurveToPoint:pointAfterTopCurve controlPoint:topRightCorner];
[path addLineToPoint:pointBeforeBottomCurve];
[path addQuadCurveToPoint:pointAfterBottomCurve controlPoint:bottomRightCorner];
[path addLineToPoint:pointBeforeArrow];
[path addLineToPoint:arrowPoint];
[path addLineToPoint:startPoint];
[path fill];