Using blocks to simplify custom view drawingCommonly when you are drawing a custom Cocoa view, you will be creating paths, filling and stroking them etc. You might even be drawing a similar shape a number of times. A naive approach to drawing five round rects with shadows might look like this: #define DDOFFSET 10 #define DDWIDTH 50 - (void)drawRect:(NSRect)dirtyRect { [NSGraphicsContext saveGraphicsState]; for (int i=0 ; i < 5 ; i++) { // offset and put onto pixel boundary CGFloat top = [self frame].size.height - 0.5 - DDOFFSET; CGFloat left = 0.5 + (DDWIDTH * i) + (DDOFFSET * i) + DDOFFSET; NSRect borderRect = NSMakeRect(left, DDOFFSET, DDWIDTH, top - DDOFFSET); NSBezierPath *borderPath = [NSBezierPath bezierPathWithRoundedRect:borderRect xRadius:5 yRadius:5]; NSColor *borderColor = [NSColor grayColor]; NSShadow *borderShadow = [[NSShadow alloc] init]; [borderShadow setShadowOffset:NSMakeSize(2.0, -2.0)]; [borderShadow setShadowBlurRadius:2.0]; [borderShadow setShadowColor:[[NSColor blackColor] colorWithAlphaComponent:0.3]]; NSColor *fillColor = [NSColor whiteColor]; // do the drawing [borderColor set]; [borderShadow set]; [borderPath stroke]; [fillColor set]; [borderPath fill]; } [NSGraphicsContext restoreGraphicsState]; } (Note these examples are all using garbage collection). Hopefully, it should occur to you that, amongst other issues, this is needlessly inefficient because a bunch of objects are being created and destroyed each time the view is dirtied. So we want to move the creation of objects into a separate method that is called when the view is created, store the paths in an array and the drawRect would then just have to set the colours, stroke the paths, etc. Something like this: @interface DrawDemo2View : NSView { NSColor *borderColor; NSShadow *borderShadow; NSColor *fillColor; NSMutableArray *borderPaths; } @end @implementation DrawDemo2View #define DDOFFSET 10 #define DDWIDTH 50 - (id)initWithFrame:(NSRect)frame { self = [super initWithFrame:frame]; if (self) { borderColor = [NSColor grayColor]; borderShadow = [[NSShadow alloc] init]; [borderShadow setShadowOffset:NSMakeSize(2.0, -2.0)]; [borderShadow setShadowBlurRadius:2.0]; [borderShadow setShadowColor:[[NSColor blackColor] colorWithAlphaComponent:0.3]]; fillColor = [NSColor whiteColor]; borderPaths = [[NSMutableArray alloc] initWithCapacity:5]; for (int i=0 ; i < 5 ; i++) { // offset and put onto pixel boundary CGFloat top = [self frame].size.height - 0.5 - DDOFFSET; CGFloat left = 0.5 + (DDWIDTH * i) + (DDOFFSET * i) + DDOFFSET; NSRect borderRect = NSMakeRect(left, DDOFFSET, DDWIDTH, top - DDOFFSET); NSBezierPath *borderPath = [NSBezierPath bezierPathWithRoundedRect:borderRect xRadius:5 yRadius:5]; [borderPaths addObject:borderPath]; } } return self; } - (void)drawRect:(NSRect)dirtyRect { [NSGraphicsContext saveGraphicsState]; for (NSBezierPath *path in borderPaths) { // do the drawing [borderColor set]; [borderShadow set]; [path stroke]; [fillColor set]; [path fill]; } [NSGraphicsContext restoreGraphicsState]; } @end That's mildly painful, and disconnects the meaning of the above code somewhat, but it's not too bad. What, though, if there are other shapes that require different handling - well they will need their own array and drawing code. Then we want to subclass the view and the subclass wants to draw circles, etc. etc. Your view will become messy and very specific. Also in a real example I'd probably need more save/restore graphics states, but here the shadow on the fill isn't important. Thankfully we can use blocks, which are what some other languages call closures, to keep the efficiency of separating the execution time of the drawing code while keeping it all together in a very general way. We end up with something almost identical to the first naive implementation, but the drawing section at the end goes into a block in an array, and the drawRect simply executes all those blocks: @interface DrawDemoBlocksView : NSView { NSMutableArray *drawingBlocks; } - (void)addRoundRect: (int)num; @end @implementation DrawDemoBlocksView #define DDOFFSET 10 #define DDWIDTH 50 - (id)initWithFrame:(NSRect)frame { self = [super initWithFrame:frame]; if (self) { drawingBlocks = [[NSMutableArray alloc] initWithCapacity:5]; for (int i=0 ; i < 5 ; i++) [self addRoundRect:i]; } return self; } - (void)drawRect:(NSRect)dirtyRect { for (void *(^block)(void) in drawingBlocks) { [NSGraphicsContext saveGraphicsState]; block(); [NSGraphicsContext restoreGraphicsState]; } } - (void)addRoundRect: (int) num { // offset and put onto pixel boundary CGFloat top = [self frame].size.height - 0.5 - DDOFFSET; CGFloat left = 0.5 + (DDWIDTH * num) + (DDOFFSET * num) + DDOFFSET; NSRect borderRect = NSMakeRect(left, DDOFFSET, DDWIDTH, top - DDOFFSET); NSBezierPath *borderPath = [NSBezierPath bezierPathWithRoundedRect:borderRect xRadius:5 yRadius:5]; NSColor *borderColor = [NSColor grayColor]; NSShadow *borderShadow = [[NSShadow alloc] init]; [borderShadow setShadowOffset:NSMakeSize(2.0, -2.0)]; [borderShadow setShadowBlurRadius:2.0]; [borderShadow setShadowColor:[[NSColor blackColor] colorWithAlphaComponent:0.3]]; NSColor *fillColor = [NSColor whiteColor]; [drawingBlocks addObject:[^{ // do the drawing [borderColor set]; [borderShadow set]; [borderPath stroke]; [fillColor set]; [borderPath fill]; } copy]]; } @end You can also see how this would work really well if we subclassed this view - all subclasses could simply push a block onto the array to request some work to be done during the drawRect phase. You'll note how the block itself isn't added to the array, but a copy of it. That's because by default the storage for the context embodied in the block is only valid for the scope in which it is created. If we want to execute the block after the scope is gone we need to copy it. If you found this useful you might also like Drew McCormack's excellent article 10 Uses for Blocks in C/Objective-C. 01:36 PM, 30 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) Afternoon FogThere was an unusual afternoon fog in Sydney today Series home: http://mark.aufflick.com/a-day-in-sydney Taken with Canon EOS 400D DIGITAL and Sigma HSM 30mm f1.4 on 2010:05:25 16:27:40
10:29 PM, 25 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) Purple BridgeDaily Shoot #ds190 'Today's color is purple.' Taken as part of The Daily Shoot project. View all Mark's Daily Shoot photos. Taken with Canon EOS 400D DIGITAL and Sigma HSM 30mm f1.4 on 2010:05:25 13:02:35
10:20 PM, 25 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) Stairway to LawThe new Sydney University Law Faculty building offers many interesting views. This one has a slight Jeffrey Smartt aspect (I like to think!) Series home: http://mark.aufflick.com/a-day-in-sydney Taken with Canon EOS 400D DIGITAL and Sigma HSM 30mm f1.4 on 2010:05:24 17:23:15
11:00 PM, 24 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) Members OnlyCan't be too many members - look how small the club house is! Series home: http://mark.aufflick.com/a-day-in-sydney Taken with Canon EOS 400D DIGITAL and Sigma HSM 30mm f1.4 on 2010:05:19 16:14:33
09:10 PM, 20 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) Roller SkatingI was sad when they closed down my childhood roller skating rink. I'm not sure this is the same. Series home: http://mark.aufflick.com/a-day-in-sydney Taken with Canon EOS 400D DIGITAL and Sigma HSM 30mm f1.4 on 2010:05:19 16:07:29
06:45 PM, 19 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) Walkway to St JamesI was at St James station early enough to find it nearly empty. St James is one of the (very few) genuinely attractive stations in Sydney. Another day I'll take some shots of the cavernous interior of the cut-and-cover underground station. Series home: http://mark.aufflick.com/a-day-in-sydney Taken with Canon EOS 400D DIGITAL and EF24-85mm f/3.5-4.5 USM on 2010:05:18 07:14:36
09:52 PM, 18 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) GloomyDaily Shoot #ds183 'Black can be dramatic. Go for as much black in a photo as you can with lots of oomph, character, and depth.' Taken as part of The Daily Shoot project. View all Mark's Daily Shoot photos. Taken with Canon EOS 400D DIGITAL and EF24-85mm f/3.5-4.5 USM on 2010:05:18 16:47:45
07:10 PM, 18 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) Chair ShadowDaily Shoot #ds180 'Find an interesting shadow--any shadow--and make a photograph of it. Consider your framing as you shoot.' Taken as part of The Daily Shoot project. View all Mark's Daily Shoot photos. Taken with Canon EOS 400D DIGITAL and Sigma HSM 30mm f1.4 on 2010:05:15 15:02:50
03:36 PM, 15 May 2010 by Mark Aufflick Permalink | Short Link | Comments (2) Coffee TimesDaily Shoot #ds178 'Pick an everyday subject and get as close as you can to it.' - This is my daily bootup - a one cup stove top espresso maker. Taken as part of The Daily Shoot project. View all Mark's Daily Shoot photos. Taken with Canon EOS 400D DIGITAL and EF24-85mm f/3.5-4.5 USM on 2010:05:13 13:57:10
02:44 PM, 13 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) Rusted PolesI've been sick for a few days, hence the pause in photographs. This is another one from the archives, taken at Dee Why beach in Sydney's North. It's a mounted transparency so I can't see what type of film it is. Series home: http://mark.aufflick.com/a-day-in-sydney Taken with Canon EOS 400D DIGITAL and EF24-85mm f/3.5-4.5 USM
10:54 AM, 13 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) Vines on the RocksI found this vine covered facade in one of the anonymous lanes of The Rocks--the oldest part of Sydney Series home: http://mark.aufflick.com/a-day-in-sydney Taken with Canon EOS 400D DIGITAL and EF24-85mm f/3.5-4.5 USM on 2010:05:07 14:17:01
10:22 PM, 07 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) View from Macquarie LighthouseToday I had no time for photographing so this is one from the archives. To be honest I can't remember when it was taken, but I do know it was taken with my EOS 30 with EF24-85mm f/3.5-4.5 USM lens using a 400 ISO Kodak C-31 process black and white film. Series home: http://mark.aufflick.com/a-day-in-sydney 11:57 PM, 06 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) Clouds over the harbourThe weather was pretty changeable today, but even so the Sydney Harbour manages to look beautiful. Series home: http://mark.aufflick.com/a-day-in-sydney Taken with Canon EOS 400D DIGITAL and EF24-85mm f/3.5-4.5 USM on 2010:05:05 10:49:53
09:55 PM, 05 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) The SacrificeRayner Hoff's amazing sculpture in the Sydney ANZAC War Memorial The central motif of the design is ‘The Sacrifice’. It comprises a bronze group of sculptures depicting the recumbent figure of a young warrior who has made the supreme sacrifice; his naked body lies upon a shield which is supported by three womenfolk - his best loved Mother, Wife and Sister and in the arms of one is a child, the future generations for whom the sacrifice has been made. Series home: http://mark.aufflick.com/a-day-in-sydney Taken with Canon EOS 400D DIGITAL and Sigma HSM 30mm f1.4 on 2010:05:04 16:42:02
10:19 PM, 04 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) Alone on Eastern AvenueA lone student at night, walking down Eastern Avenue of Sydney University, with the beautiful Anderson Stuart building as the backdrop Series home: http://mark.aufflick.com/a-day-in-sydney Taken with Canon EOS 400D DIGITAL and Sigma HSM 30mm f1.4 on 2010:05:03 17:47:55
10:40 PM, 03 May 2010 by Mark Aufflick Permalink | Short Link | Comments (0) |
Archive
November 2011 October 2011 April 2011 March 2011 January 2011 November 2010 October 2010 September 2010 August 2010 July 2010 June 2010 May 2010 April 2010 March 2010 February 2010 January 2010 October 2009 September 2009 August 2009 July 2009 June 2009 May 2009 April 2009 February 2009 January 2009 December 2008 November 2008 October 2008 September 2008 August 2008 July 2008 June 2008 May 2008 March 2008 February 2008 January 2008 December 2007 November 2007 October 2007 September 2007 August 2007 July 2007 June 2007 May 2007 April 2007 March 2007 February 2007 January 2007 December 2006 November 2006 October 2006 September 2006 August 2006 July 2006 June 2006 May 2006 April 2006 March 2006 February 2006 January 2006 December 2005 November 2005 October 2005 September 2005 August 2005 July 2005 June 2005 May 2005 April 2005 March 2005 February 2005 January 2005 December 2004 November 2004 October 2004 September 2004 August 2004 July 2004 June 2004 May 2004 April 2004 March 2004 February 2004 January 2004 December 2003 November 2003 October 2003 September 2003 August 2003 Blog Categoriessoftware (39)..cocoa (21) ..heads up 'tunes (5) ..ruby (6) ..lisp (3) ..perl (4) ..openacs (1) mac (20) embedded (2) ..microprocessor (2) ..avr (1) electronics (3) design (1) photography (25) ..black and white (6) ..A day in Sydney (18) ..The Daily Shoot (6) food (2) Book Review (2) Notifications Request notifications
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||























Request notifications