agnsaft agnsaft - 1 year ago 145
Objective-C Question

Drawing board/grid with Cocoa

I am writing a small boardgame for Mac OS X using Cocoa. I the actual grid is drawn as follows:

- (void)drawRect:(NSRect)rect
for (int x=0; x < GRIDSIZE; x++) {
for (int y=0; y < GRIDSIZE; y++) {

float ix = x*cellWidth;
float iy = y*cellHeight;

NSColor *color = (x % 2 == y % 2) ? boardColors[0] : boardColors[1];
[color set];

NSRect r = NSMakeRect(ix, iy, cellWidth, cellHeight);

NSBezierPath *path = [NSBezierPath bezierPath];
[path appendBezierPathWithRect:r];

[path fill];
[path stroke];

This works great, except that I see some errors in colors between the tiles. I guess this is due to some antialiasing or similar. See screenshots below (hopefully you can also see the same problems... its some black lines where the tiles overlap):

rendered board

Therefore I have these questions:

  1. Is there any way I can remove these graphical artefacts while still maintaining a resizable/scalable board?

  2. Should I rather use some other graphical library like Core Graphics or OpenGL?


const int GRIDSIZE = 16;
cellWidth = (frame.size.width / GRIDSIZE);
cellHeight = (frame.size.height / GRIDSIZE);

Answer Source

If you want crisp rectangles you need to align coordinates so that they match the underlying pixels. NSView has a method for this purpose: - (NSRect)backingAlignedRect:(NSRect)aRect options:(NSAlignmentOptions)options. Here's a complete example for drawing the grid:

const NSInteger GRIDSIZE = 16;

- (void)drawRect:(NSRect)dirtyRect {
    for (NSUInteger x = 0; x < GRIDSIZE; x++) {
        for (NSUInteger y = 0; y < GRIDSIZE; y++) {
            NSColor *color = (x % 2 == y % 2) ? [NSColor greenColor] : [NSColor redColor];
            [color set];
            [NSBezierPath fillRect:[self rectOfCellAtColumn:x row:y]];

- (NSRect)rectOfCellAtColumn:(NSUInteger)column row:(NSUInteger)row {
    NSRect frame = [self frame];
    CGFloat cellWidth = frame.size.width / GRIDSIZE;
    CGFloat cellHeight = frame.size.height / GRIDSIZE;
    CGFloat x = column * cellWidth;
    CGFloat y = row * cellHeight;
    NSRect rect = NSMakeRect(x, y, cellWidth, cellHeight);
    NSAlignmentOptions alignOpts = NSAlignMinXNearest | NSAlignMinYNearest |
            NSAlignMaxXNearest | NSAlignMaxYNearest ;
    return [self backingAlignedRect:rect options:alignOpts];

Note that you don't need stroke to draw a game board. To draw pixel aligned strokes you need to remember that coordinates in Cocoa actually point to lower left corners of pixels. To crisp lines you need to offset coordinates by half a pixel from integral coordinates so that coordinates point to centers of pixels. For example to draw a crisp border for a grid cell you can do this:

NSRect rect = NSInsetRect([self rectOfCellAtColumn:column row:row], 0.5, 0.5);
[NSBezierPath strokeRect:rect];
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download