In a recent
post with
followup,
Mark Granoff demonstrates how to intelligently deal with the need for
higher resolution backgrounds by using CoreGraphics pattern images, particularly using the [UIColor colorWithPatternImage:] method. However, he does wonder why he still has to deal with retina resolution issues at some points in the code, when "…the docs say that CoreGraphics handles scaling issues automatically."
That's a good question, and the answer lies in the fact that the example uses pattern images and
mask images, rather than CoreGraphics patterns and geometric primitives. Once you explicitly
ask for bitmap representations, you will be dealing with pixels and different resolution. The clue is
to avoid going to pixels as much and as long as possible.
The doughnut shape, for example, can easily be achieved using basic geometry and a little knowledge
of the Postscript/PDF fill rules.
Using the standard "nonzero-winding-number" rule, a doughnut effect can be achieved by having the
two arcs that are nested inside each other drawn in opposite directions. That's one of the reasons
the extra "clockwise" parameter exists.
NSPoint centerPoint = NSMakePoint([view frame].size.width/2, 150);
[context arcWithCenter:centerPoint
radius:50
startDegrees:0
endDegrees:360
clockwise:YES];
[context arcWithCenter:centerPoint
radius:100
startDegrees:0
endDegrees:360
clockwise:NO];
[context fill];
(The code examples here use
MPWDrawingContext for convenience, pure CoreGraphics code tends to
be two to three times more verbose). The second way to achieve the doughnut would be to just
use the even/odd fill rule, in which case the direction doesn't matter.
matter.
Patterns can also be specified geometrically, or rather with callbacks to draw the
pattern shape. Objective-C Blocks are really a perfect fit for specifying these sorts of
callbacks, but were only introduced much later than the CoreGraphics pattern callback API.
The following code shows how to specify the diamond pattern via an Objective-C block,
courtesy of some glue API provided by MPWDrawingContext.
NSSize patternSize=NSMakeSize(16,16);
id diamond = [context laterWithSize:patternSize
content:^(id context){
id red = [context colorRed:1.0 green:0.0 blue:0.0 alpha:1.0];
[context setFillColor:red];
[[context moveto:patternSize.width/2 :2]
lineto:patternSize.width-2 :patternSize.height/2];
[[context lineto:patternSize.width/2 :patternSize.height-2]
lineto:2 :patternSize.height/2];
[[context closepath] fill];
}];
[context setFillColor:diamond];
[[context nsrect:[[self view] frame]] fill];
The "laterWithSize:content:" message creates a callback object that not only encapsulates the block,
but also implements a -CGColor method so the callback can be used directly as a color in -setFillColor:.
With all the graphics specified using pure geometry, CoreGraphics can now do its thing and
automatically handle varying device resolutions, wether it's a retina display or a
zoomable interface or even print, all without ever having to deal with the different
resolutions in code. Although I haven't tested it, the code should also use less
memory, because it doesn't create potentially large temporary bitmaps, and for the
cherry on top it's also a fraction of the code. CoreGraphics rules!
Forked project on github.