Sunday, July 18, 2010

Drawing Patterns

Imagine you have a nice wooden texture that you want to use in some kind of navigation bar. You want the wooden texture to be repeatedly drawn along the x-axis. Your first approach might look something like this.

#import "PatternView.h"

@implementation PatternView

- (void)drawRect:(NSRect)dirtyRect {
   [superdrawRect:dirtyRect];

   NSImage *bg = [NSImageimageNamed:@"bg"];
   NSColor *backgroundColor = [NSColorcolorWithPatternImage:bg];
[backgroundColor set];
   NSRectFill([selfbounds]);
}

@end

This might not be what you have expected. Let me show you how the above code behaves in action.

Pattern1-Web.jpg

(Click on the image above to see the video)

As you can see when resizing the window the displayed texture is changing as well. This is because of the fact that patterns are always drawn relative to the containing window. Of course this works fine for patterns that should repeat along both axes. In this case however, we don't want that. Let me show you how to fix that.

#import "PatternView.h"

@implementation PatternView

- (void)drawRect:(NSRect)dirtyRect {
   [superdrawRect:dirtyRect];
   NSSize size = [selfbounds].size;

   // Create a new image and make it as big as the view
   NSImage *bigImage = [[[NSImagealloc] initWithSize:size] autorelease];

   // Prepare for drawing onto the new image
   [bigImage lockFocus];

   NSImage *bg = [NSImageimageNamed:@"bg"];
   NSColor *backgroundColor = [NSColorcolorWithPatternImage:bg];
   [backgroundColor set];
   NSRectFill([selfbounds]);

   [bigImage unlockFocus];

   // Now draw the image to the view
   [bigImage drawInRect:[selfbounds
               fromRect:NSZeroRect 
              operation:NSCompositeSourceOver 
               fraction:1.0f];
}

@end


The trick is to create a new empty image and size it correctly. Then you simply have to draw as shown in the first example. This results in exactly what we want.

Pattern2-Web.jpg
(Click on the above image to see the video)

Feel free to download the examples.