NSDrawNinePartImage

There is a cool new function in Leopard for drawing scaled rectangles of arbitrary width and height that keeps the corner and sides properly scaled while growing the center.

Unfortunately there is no documentation, and when I last looked a whopping 3 google hits for the function name. This should make it number four.

If you want to draw a well or button in a view is to start with a bitmap image the same size as your view. But what to do if you want to write a reusable class that can work at any size? And how to deal with resolution independence?

NSDrawNinePartImage solves the problem. To use it you create a 3×3 matrix of images. You can either create them independently, or just draw a single image in PhotoShop and then break it apart in code.

Here is some sample code that appears in the OneMinuteEggTimer example application that is used for demonstrating the sound picker.

24x24.gif

Start with the sample image. In this case the image in 24×24 pixels, but I’ve blown the image up to make it clearer what is going on.

32x32.gif

The first step is to break the image into a 3×3 matrix. The corners will be drawn as is, the sides will each be tiled in one dimension, and the center will be tiled in two dimensions. You can either break it using Photoshop, or programatically when your class is first used.

The +initialize method is called the first time your view class is referenced. This is a function of the Objective-C runtime. This code will take an image named SoundPickerBackground, and break it into 9 8×8 tiles.


static NSImage *topLeftCornerImage;
static NSImage *topEdgeImage;
static NSImage *topRightCornerImage;
static NSImage *leftEdgeImage;
static NSImage *centerImage;
static NSImage *rightEdgeImage;
static NSImage *bottomLeftCornerImage;
static NSImage *bottomEdgeImage;
static NSImage *bottomRightCornerImage;

+ (void)initialize;
{
  if (baseImage) return;

  NSRect tileRect = NSMakeRect(0,0,8,8);

  baseImage = [NSImage imageNamed:@"SoundPickerBackground"];

  topLeftCornerImage = [[NSImage alloc] initWithSize:tileRect.size];
  [topLeftCornerImage lockFocus];
  [baseImage drawInRect:tileRect
               fromRect:NSMakeRect(0.0,16.0,8.0,8.0)
              operation:NSCompositeCopy fraction:1.0];
  [topLeftCornerImage unlockFocus];

  topEdgeImage = [[NSImage alloc] initWithSize:tileRect.size];
  [topEdgeImage lockFocus];
  [baseImage drawInRect:tileRect
               fromRect:NSMakeRect(8.0,16.0,8.0,8.0)
              operation:NSCompositeCopy fraction:1.0];
  [topEdgeImage unlockFocus];

  topRightCornerImage = [[NSImage alloc] initWithSize:tileRect.size];
  [topRightCornerImage lockFocus];
  [baseImage drawInRect:tileRect
               fromRect:NSMakeRect(16.0,16.0,8.0,8.0)
              operation:NSCompositeCopy fraction:1.0];
  [topRightCornerImage unlockFocus];

  leftEdgeImage = [[NSImage alloc] initWithSize:tileRect.size];
  [leftEdgeImage lockFocus];
  [baseImage drawInRect:tileRect
               fromRect:NSMakeRect(0,8.0,8.0,8.0)
              operation:NSCompositeCopy fraction:1.0];
  [leftEdgeImage unlockFocus];

  centerImage = [[NSImage alloc] initWithSize:tileRect.size];
  [centerImage lockFocus];
  [baseImage drawInRect:tileRect
               fromRect:NSMakeRect(8.0,8.0,8.0,8.0)
              operation:NSCompositeCopy fraction:1.0];
  [centerImage unlockFocus];

  rightEdgeImage = [[NSImage alloc] initWithSize:tileRect.size];
  [rightEdgeImage lockFocus];
  [baseImage drawInRect:tileRect
               fromRect:NSMakeRect(16.0,8.0,8.0,8.0)
              operation:NSCompositeCopy fraction:1.0];
  [rightEdgeImage unlockFocus];

  bottomLeftCornerImage = [[NSImage alloc] initWithSize:tileRect.size];
  [bottomLeftCornerImage lockFocus];
  [baseImage drawInRect:tileRect
               fromRect:NSMakeRect(0,0,8.0,8.0)
              operation:NSCompositeCopy fraction:1.0];
  [bottomLeftCornerImage unlockFocus];

  bottomEdgeImage = [[NSImage alloc] initWithSize:tileRect.size];
  [bottomEdgeImage lockFocus];
  [baseImage drawInRect:tileRect
               fromRect:NSMakeRect(8.0,0,8.0,8.0)
              operation:NSCompositeCopy fraction:1.0];
  [bottomEdgeImage unlockFocus];

  bottomRightCornerImage = [[NSImage alloc] initWithSize:tileRect.size];
  [bottomRightCornerImage lockFocus];
  [baseImage drawInRect:tileRect
               fromRect:NSMakeRect(16.0,0,8.0,8.0)
              operation:NSCompositeCopy fraction:1.0];
  [bottomRightCornerImage unlockFocus];
}

Now when you need to draw the nine tiles, you simple call NSDrawNinePartImage in drawRect:

- (void)drawRect:(NSRect)rect;
{
  NSDrawNinePartImage([self bounds],
                      topLeftCornerImage, topEdgeImage, topRightCornerImage,
                      leftEdgeImage, centerImage, rightEdgeImage,
                      bottomLeftCornerImage, bottomEdgeImage, bottomRightCornerImage,
                      NSCompositeSourceOver, 1.0, NO);
}

Drawn as 48×48

48x48.gif

Drawn as 128×128

128x128.gif
This entry was posted in TheCodeBook - Snippets. Bookmark the permalink. Follow any comments here with the RSS feed for this post. Both comments and trackbacks are currently closed.
  • iChat Status