Drawing in Obj-C: Lines and Circles
Day 14
In a previous post I showed an example of creating a custom view in which I drew a triangle on screen. I thought I’d spend a bit of time taking a closer look at the code and demonstrate how to draw simple shapes in Objective-C.
Example 1: Lines
The following will draw a triangle in the view with green fill and a red stroke. I’ve added a bunch of comments to each line to make it clear what is happening.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
- (void)drawRect:(CGRect)rect
{
// Get the current graphics context
// (ie. where the drawing should appear)
CGContextRef context = UIGraphicsGetCurrentContext();
//Begin the path
CGContextBeginPath(context);
// Starting Point
CGContextMoveToPoint(context, 150,0);
// Draw a diagonal line starting ay 150, 0 and ending at x300,y300
CGContextAddLineToPoint(context, 300, 300);
// Create a straight horizontal line from the end of the last point, over to x0, y300
CGContextAddLineToPoint(context, 0, 300);
// Close the path
// Closing the path will extending a line from
// x0, y300 back up to the starting point of 150, 0
CGContextClosePath(context);
// Set line width
CGContextSetLineWidth(context, 2.0);
// Set colour using RGB intensity values
// 1.0 = 100% red, green or blue
// the last value is alpha
CGContextSetRGBFillColor(context, 0.0, 1.0, 0.0, 1.0); //green
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0); //red
//Draw on the screen
CGContextDrawPath(context, kCGPathFillStroke);
} |
The above will draw something like below:
Example 2: Circles
Next I’ll draw a red circle. Again I’ve added some comments for each line.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
- (void)drawRect:(CGRect)rect
{
// Get the current graphics context
// (ie. where the drawing should appear)
CGContextRef context = UIGraphicsGetCurrentContext();
// Set the width of the line
CGContextSetLineWidth(context, 2.0);
//Make the circle
// 150 = x coordinate
// 150 = y coordinate
// 100 = radius of circle
// 0 = starting angle
// 2*M_PI = end angle
// YES = draw clockwise
CGContextBeginPath(context);
CGContextAddArc(context, 150, 150, 100, 0, 2*M_PI, YES);
CGContextClosePath(context);
// Set colour using RGB intensity values
// 1.0 = 100% red, green or blue
// the last value is alpha
CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0); //blue
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0); //red
// Note: If I wanted to only stroke the path, use:
// CGContextDrawPath(context, kCGPathStroke);
// or to only fill it, use:
// CGContextDrawPath(context, kCGPathFill);
//Fill/Stroke the path
CGContextDrawPath(context, kCGPathFillStroke);
} |
The above will produce:
Using Convenience Methods for Drawing
Say I want to make 3 circles on the screen, each of them with a slightly smaller radius. Well, I could simply copy and paste the code for making a circle 3 times, but it would be better if I could write a single drawCircle: method that will make one for me 3 times.
In my drawRect: I would do something like this:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
- (void)drawRect:(CGRect)rect
{
// Get the current graphics context
CGContextRef context = UIGraphicsGetCurrentContext();
// Clear whatever is currently displayng (optional)
CGContextClearRect(context, rect);
[self drawCircleInContext:context withRadius:90];
[self drawCircleInContext:context withRadius:60];
[self drawCircleInContext:context withRadius:30];
} |
Note I’m grabbing the current context first before calling the circle drawing method with:
|
1 |
CGContextRef context = UIGraphicsGetCurrentContext(); |
This is important and must be called within drawRect:. Then, I am calling a new method three times, each with a slightly smaller radius:
|
1 2 3 |
[self drawCircleInContext:context withRadius:90];
[self drawCircleInContext:context withRadius:60];
[self drawCircleInContext:context withRadius:30]; |
The drawCircleInContext:withRadius: method would look something like this:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
-(void)drawCircleInContext:(CGContextRef)context withRadius:(int)radius {
// always add at start to make sure
// specified context context is the current one
UIGraphicsPushContext(context);
// DRAW CIRCLE
// Note: This code will draw the circle and
// is the same as before. The only difference
// is that I've used the radius int within
// CGContextAddArc
CGContextSetLineWidth(context, 2.0);
CGContextBeginPath(context);
CGContextAddArc(context, 150, 150, radius, 0, 2*M_PI, YES);
CGContextClosePath(context);
CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0); //blue
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0); //red
CGContextDrawPath(context, kCGPathFillStroke);
// END DRAW CIRCLE
// Eemove current context (defined at top of method)
// from the stack, restoring it to whatever it was before
UIGraphicsPopContext();
} |
The bulk of the code will be the same as before, but at the top of the method you’ll want to add:
|
1 |
UIGraphicsPushContext(context); |
This will ensure the context you’re using is the current one. Then at the bottom of the method (after drawing the circle) you should add:
|
1 |
UIGraphicsPopContext(); |
This will remove the current context from the stack and restore it to what it was before.
If all went well, you should end up with this:
Re-Drawing
I mentioned this in my post about Custom Sub Views, but it’s worth repeating. If you want to change the appearance of your drawing, it might seem logical to call drawRect: from your master view or controller in order to re-draw.
Don’t do it!
You should never call drawRect: directly. Instead you should use setNeedsDisplay, like so:
|
1 |
[self.myCanvas setNeedsDisplay]; |
Assuming self.myCanvas is your UIView that does the drawing, the above line lets the system know that you want to redraw the myCanvas view (you could also call setNeedsDislay within the UIView class itself, of course). As soon as it is ready the view will be updated.
Here’s an example of how you might trigger this from a view controller:
|
1 2 3 |
- (IBAction)updateCanvas:(id)sender {
[self.myCanvas setNeedsDisplay];
} |
Download Project File
In case it helps I’ve made the Xcode project fie available to download below. The app produces either a triangle or circle depending on which button you tap.



