Hey, I'm trying to figure out how to generate bezier curves in iOS based on user input. Are there any existing classes for this? Can someone give me a general summary of what would be required? I just need help getting started on the right foot.
-
Thats what you need. Just take a look this tutorial named [How to build a Simple Painting App for iOS](http://blog.effectiveui.com/?p=8105) – fyasar Aug 07 '12 at 10:12
-
2There's a full writeup on this over at Mike Nachbaur's blog. [You can find the post here](http://nachbaur.com/blog/core-animation-part-4), and it's all about animating objects over user-drawn bezier curves. – Neal L Jan 12 '11 at 19:35
-
The following is a copy of my answer [on a related question](http://stackoverflow.com/questions/7345251/bezier-path-with-dynamic-change-of-width/11091956#11091956). I have written some functions that will help you do exactly that. Read [more on my post](http://www.wiggler.gr/2012/06/17/bezier-curves-control-points-calculation-not-only-in-ios/). The code is on [my repo at github](https://github.com/Petrakeas/outlineDEMO). In my post you'll also find [a video](http://www.youtube.com/watch?v=iOHvIiryfaQ) demonstrating the usage of the functions. – Petrakeas Jun 18 '12 at 22:49
-
[http://mobile.tutsplus.com/tutorials/iphone/ios-sdk_freehand-drawing/](http://mobile.tutsplus.com/tutorials/iphone/ios-sdk_freehand-drawing/) this link is very useful. IT uses UIBezierPath class to draw lines and curves smoothely with fingure touch – YogiAR Feb 06 '13 at 10:44
2 Answers
If you want to stay in objective-c, you can use UIBezierPath's addCurveToPoint:controlPoint1:controlPoint2: method. You can also use a similarly named function with CGPaths. When using bezier curves, you need 4 points: starting point, ending point, and a control point at each end to define the curve.
One way to define this is to have the user drag a finger to define the start and end points, then tap the screen at the control points. Here is an example view to handle this.
BezierView.h
enum {
BezierStateNone = 0,
BezierStateDefiningLine,
BezierStateDefiningCP1,
BezierStateDefiningCP2
};
@interface BezierView : UIView {
CGPoint startPt, endPt, cPt1, cPt2;
UInt8 state;
UIBezierPath *curvePath;
@private
UITouch *currentTouch;
}
@property (nonatomic, retain) UIBezierPath *curvePath;
@end
BezierView.m
@interface BezierView
@dynamic curvePath;
- (UIBezierPath *)curvePath {
return [[curvePath retain] autorelease];
}
- (void)setCurvePath:(UIBezierPath *)newPath {
id tmp = curvePath;
curvePath = [newPath retain];
[tmp release];
state = BezierStateNone;
[self setNeedsDisplay];
}
- (void)_updateCurve {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:startPt];
[path addCurveToPoint:endPt controlPoint1:cPt1 controlPoint2:cPt2];
}
- (void)_calcDefaultControls {
if(ABS(startPt.x - endPt.x) > ABS(startPt.y - endPt.y)) {
cPt1 = (CGPoint){(startPt.x + endPt.x) / 2, startPt.y};
cPt2 = (CGPoint){cPt1.x, endPt.y};
} else {
cPt1 = (CGPoint){startPt.x, (startPt.y + endPt.y) / 2};
cPt2 = (CGPoint){endPt.x, cPt1.y};
}
}
- (void)drawRect:(CGRect)rect {
UIBezierPath *path = self.curvePath;
if(path) [path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(currentTouch) return;
if(state == BezierStateNone) {
state = BezierStateDefiningLine;
currentTouch = [touches anyObject];
startPt = [currentTouch locationInView:self];
} else if(state == BezierStateDefiningCP1) {
currentTouch = [touches anyObject];
cPt1 = [currentTouch locationInView:self];
[self _updateCurve];
} else if(state == BezierStateDefiningCP2) {
currentTouch = [touches anyObject];
cPt2 = [currentTouch locationInView:self];
[self _updateCurve];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(!currentTouch) return;
if(state == BezierStateDefiningLine) {
endPt = [currentTouch locationInView:self];
[self _calcDefaultControls];
[self _updateCurve];
} else if(state == BezierStateDefiningCP1) {
cPt1 = [currentTouch locationInView:self];
[self _updateCurve];
} else if(state == BezierStateDefiningCP2) {
cPt2 = [currentTouch locationInView:self];
[self _updateCurve];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(!currentTouch) return;
if(state == BezierStateDefiningLine) {
state = BezierStateDefiningCP1;
} else if(state == BezierStateDefiningCP1) {
state = BezierStateDefiningCP2;
} else if(state == BezierStateDefiningCP2) {
state = BezierStateNone;
}
currentTouch = nil;
}
- (void)touchesCanceled:(NSSet *)touches withEvent:(UIEvent *)event {
if(state == BezierStateDefiningLine) {
self.curvePath = nil;
self.state = BezierStateNone;
}
self.currentTouch = nil;
}

- 39,734
- 6
- 101
- 123
Okay, the easiest way to do that is probably subclassing UIView
and use CoreGraphics for drawing. Check out the sample code from QuarzDemo.
Implement the drawInRect
-method for your custom view class. And detect the user's touches with touchesBegan
,touchesMoved
etc.
Here is an example code (taken from QuarzDemo) for drawing a bezier curve:
// Drawing with a white stroke color
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
// Draw them with a 2.0 stroke width so they are a bit more visible.
CGContextSetLineWidth(context, 2.0);
// Draw a bezier curve with end points s,e and control points cp1,cp2
CGPoint s = CGPointMake(30.0, 120.0);
CGPoint e = CGPointMake(300.0, 120.0);
CGPoint cp1 = CGPointMake(120.0, 30.0);
CGPoint cp2 = CGPointMake(210.0, 210.0);
CGContextMoveToPoint(context, s.x, s.y);
CGContextAddCurveToPoint(context, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);
CGContextStrokePath(context);
Hope that helps you getting started ;)

- 35,354
- 13
- 96
- 143
-
1phix23: thanks for the extremely helpful reply. If I want a smoothly interpolated curve, will I need to develop an algorithm for that myself? I want the curve generated using as few vertices as possible. – Brendan Jan 12 '11 at 19:27
-
Hello @phix23 please have look at this http://stackoverflow.com/questions/20881721/calculate-controlpoints-while-drawing-in-ios and please give your expert advise – Ranjit Jan 02 '14 at 12:27