Drawing Within a UIKit Context
UIKit simplifies the task of creating and managing contexts. It takes just one line to establish a new image or PDF context, and another to wrap things up. Between those lines, you are free to apply any drawing requests. These are applied to the current context.
Listing 1-6 applies the same drawing functions as in Listing 1-5, and it produces an identical image result. However, instead of drawing to a custom Quartz context, these updates are drawn to a newly established image context. In order to bridge between Quartz and UIKit, you call UIGraphicsGetCurrentContext(). This returns a CGContextRef, enabling you to use that value in your Core Graphics drawing calls.
Once again, when you compare the number of lines of code in Listing 1-6 with the combination of Listings 1-4 and 1-5, you see how much simpler UIKit drawing can be.
Listing 1-6 Drawing an Ellipse Within a UIKit Image Context
// Establish the image context UIGraphicsBeginImageContextWithOptions( targetSize, isOpaque, 0.0); // Retrieve the current context CGContextRef context = UIGraphicsGetCurrentContext(); // Perform the drawing CGContextSetLineWidth(context, 4); CGContextSetStrokeColorWithColor(context, [UIColor grayColor].CGColor); CGContextStrokeEllipseInRect(context, rect); // Retrieve the drawn image UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); // End the image context UIGraphicsEndImageContext();
You can call UIGraphicsGetCurrentContext() after starting any image or PDF context. That context persists until you call UIGraphicsEndImageContext() or UIGraphicsEndPDFContext().
Similarly, you can establish a context outside these routines by calling UIGraphicsPushContext(context). Supply a manually built Quartz context like the one created in Listing 1-4. You end that context with UIGraphicsPopContext(). These calls establish the context you’re drawing to inside the UIKit context stack. Pushing and popping the stack enables you to temporarily switch between drawing destinations as needed.
Otherwise, outside an explicit context environment, the current context is nil—with one exception. Upon calling drawRect:, views push a context onto the UIKit graphics context stack. So if you are implementing the drawRect: method, you can assume that there’s always a valid context that you can retrieve:
- (void) drawRect: (CGRect) rect { // Perform drawing here // If called, UIGraphicsGetCurrentContext() // returns a valid context }
UIKit Current Context
In Quartz, nearly every drawing function typically requires a context parameter. You pass it explicitly to each function. For example, you might set the stroke color to gray:
CGContextSetStrokeColorWithColor(context, [UIColor grayColor].CGColor);
In UIKit drawing, context is established from runtime conditions. Consider Listing 1-7. This code once again builds the same 4-pixel-wide ellipse shown in Figure 1-5. However, nowhere does this code explicitly reference a context.
This listing creates an elliptical path. It sets the line width to 4, establishes a gray color for stroking, and then strokes the path. In each step, the context is accessed on your behalf. As with Listing 1-6, the same gray color is applied to the current context, but that context is not specified. Listing 1-7 does this without mentioning the context.
Listing 1-7 Drawing an Ellipse Within a UIKit Image Context
// Stroke an ellipse using a Bezier path UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect]; path.lineWidth = 4; [[UIColor grayColor] setStroke]; [path stroke];
What’s happening is that UIKit owns a stack of graphics contexts. It applies drawing operations to whatever context is at the top of that stack. The settings that update the context state to 4 for line width and gray for stroke color apply to that topmost context.
So how do you integrate Core Graphics contexts with UIKit? UIKit provides two key functions, which were briefly mentioned earlier in the chapter:
- You manually push a context by calling UIGraphicsPushContext(context). This function pushes the context onto the UIKit stack and sets the active context you’ll be drawing to.
- You balance this by calling UIGraphicsPopContext(). This pops the current context off the UIKit stack, resetting the active context either to the next item on the stack or to nil.
By surrounding Listing 1-7 with these calls, you can embed the Bezier path-based drawing code from Listing 1-7 into the Quartz-based context in Listing 1-3. This approach bridges UIKit drawing into Core Graphics context creation and management.
To summarize, the steps for mixing a Core Graphics context with UIKit drawing are as follows:
- Create a Core Graphics context.
- Push the context using UIGraphicsPushContext().
- Use a combination of UIKit drawing calls that infer the current context and Quartz calls that use the context explicitly.
- (Optionally retrieve the context contents as an image.)
- Pop the context with UIGraphicsPopContext().
- Release the context’s memory.
If you try to draw with UIKit with no active context, you’ll receive warnings that the context is invalid.