Paths
By issuing a series of instructions for moving an imaginary pen, you trace out a path. Such a path does not constitute drawing! First you provide a path; then you draw. Drawing can mean stroking the path or filling the path, or both. Again, this should be a familiar notion from certain drawing applications.
A path is constructed by tracing it out from point to point. Think of the drawing system as holding a pen. Then you must first tell that pen where to position itself, setting the current point; after that, you issue a series of commands telling it how to trace out each subsequent piece of the path. Each additional piece of the path starts at the current point; its end becomes the new current point.
Here are some path-drawing commands you’re likely to give:
Position the current point
CGContextMoveToPoint
Trace a line
CGContextAddLineToPoint, CGContextAddLines
Trace a rectangle
CGContextAddRect, CGContextAddRects
Trace an ellipse or circle
CGContextAddEllipseInRect
Trace an arc
CGContextAddArcToPoint, CGContextAddArc
Trace a Bezier curve with one or two control points
CGContextAddQuadCurveToPoint, CGContextAddCurveToPoint
Close the current path
CGContextClosePath. This appends a line from the last point of the path to the first point. There’s
no need to do this if you’re about to fill the path, since it’s done for you.
Stroke or fill the current path
CGContextStrokePath, CGContextFillPath, CGContextEOFillPath, CGContextDrawPath. Stroking or
filling the current path clears the path. Use CGContextDrawPath if you want both to fill and to
stroke the path in a single command,'cause if you merely stroke it first with CGContextStrokePath,
the path is cleared and you can no longer fill it.
Erase a rectangle
CGContextClearRect. This erases all existing drawing in a rectangle. When called on the current
context in a view’s drawRect:, it erases the view’s background color if there is one. If the
background color is nil or a color with some transparency, the result will be a transparent
rectangle; if the background color is opaque, the result will be a black rectangle.
There are also a lot of convenience functions that create a path and stroke or fill it all in a single move: CGContextStrokeLineSegments, CGContextStrokeRect, CGContextStrokeRectWithWidth, CGContextFillRect, CGContextFillRects, CGContextStrokeEllipseInRect, CGContextFillEllipseInRect.
If you’re worried that there might be an existing path, you can call CGContextBeginPath as you start constructing a new path.
A path can be compound, meaning that it consists of multiple independent pieces. For example, a single path might consist of two separate closed shapes: a rectangle and a circle. When you call CGContextMoveToPoint in the middle of constructing a path (that is, after tracing out a path and without clearing it by filling, stroking, or calling CGContextBeginPath), you pick up the imaginary pen and move it to a new location without tracing a segment, thus preparing to start an independent piece of the same path.
To illustrate the typical use of path-drawing commands, I’ll generate the up-pointing
arrow. This might not be the best way to create the arrow, and I’m deliberately avoiding use of the convenience functions, but it’s clear and shows a nice basic variety of typical commands:
// obtain the current graphics context
CGContextRef con = UIGraphicsGetCurrentContext();
// draw a black (by default) vertical line, the shaft of the arrow
CGContextMoveToPoint(con, 100, 100);
CGContextAddLineToPoint(con, 100, 19);
CGContextSetLineWidth(con, 20);
CGContextStrokePath(con);
// draw a red triangle, the point of the arrow
CGContextSetFillColorWithColor(con, [[UIColor redColor] CGColor]);
CGContextMoveToPoint(con, 80, 25);
CGContextAddLineToPoint(con, 100, 0);
CGContextAddLineToPoint(con, 120, 25);
CGContextFillPath(con);
// snip a triangle out of the shaft by drawing in Clear blend mode
CGContextMoveToPoint(con, 90, 101);
CGContextAddLineToPoint(con, 100, 90);
CGContextAddLineToPoint(con, 110, 101);
CGContextSetBlendMode(con, kCGBlendModeClear);
CGContextFillPath(con);
Properly speaking, we should probably surround our drawing code with calls to CGContextSaveGState and CGContextRestoreGState, just in case. It probably wouldn’t make any difference in this particular example, as the context does not persist between calls to drawRect:, but it can’t hurt.
If a path needs to be reused, you can save it as a CGPath, which is actually a CGPathRef. You can either copy the graphics context’s current path using CGContextCopyPath, or you can create a new CGMutablePathRef and construct the path using various CGPath functions that parallel the graphics path-construction functions.