Stairways Software Icon

Stairways Software

Excellence In Software For Over Ten Years – Only with a Mac

Creating an NSImage from an NSView

There are many different techniques for creating an NSImage from an NSView - finally I found one that works well!

There are various techniques:

  • lockFocus on the image, and then drawRect: the view

But this only draws the root view, not subviews.

  • lockFocus on the image, and then drawRect: the view and draw subviews

But its nigh on impossible to actually get the transformations correct for adjusting to the subview co-ordinate systems.

  • lockFocus on the view, and use initWithFocusedViewRect:

But this only gets the visible parts of the view.

  • use dataWithPDFInsideRect

But this is quite slow and has some drawing artefacts when the resulting image is displayed on the screen.

I finally found Gerd Knops’ post which yields a good result.

- (NSImage *)imageWithSubviews
 NSSize mySize = self.bounds.size;
 NSSize imgSize = NSMakeSize( mySize.width, mySize.height );
 NSBitmapImageRep *bir = [self bitmapImageRepForCachingDisplayInRect:[self bounds]];
 [bir setSize:imgSize];
 [self cacheDisplayInRect:[self bounds] toBitmapImageRep:bir];
 NSImage* image = [[[NSImage alloc]initWithSize:imgSize] autorelease];
 [image addRepresentation:bir];
 return image;

I am combining this with Matt Gemmell’s MGViewAnimation with good results for use in alpha fading views under 10.5.

Posted Tuesday, April 21, 2009. Permalink. 1 Comments.


I found an interesting bug in cacheDisplayInRect and focus ring drawing when the view you are imaging is enclosed in an NSScrollView/NSClipView (I'm not sure which, since I never use one without the other). In this case, if there is an active (firstResponder) control with a focus ring, the focus ring is clipped incorrectly. The view draws highlighted as normal, but the focus ring draws partially clipped. Eventually I determined that the offset of the clip was the same as the offset of the enclosing NSScrollView/NSClipView in the window.

I developed the following work around, which increases the bounds of the desired cached area by the appropriate offset. rdar://problem/6987153.

- (NSImage *)imageWithSubviews
 NSRect realBounds = self.bounds;
 NSSize realSize = realBounds.size;
 NSRect fakeBounds = realBounds;

 NSScrollView* hackScrollView = self.enclosingScrollView;

 NSPoint offset = NSZeroPoint; 
 if ( hackScrollView ) {
  NSPoint botLeft;
  botLeft.x = NSMinX( hackScrollView.bounds );
  botLeft.y = hackScrollView.isFlipped ? NSMaxY( hackScrollView.bounds ) : NSMinY( hackScrollView.bounds );
  offset = [hackScrollView convertPoint:botLeft toView:hackScrollView.window.contentView];
 fakeBounds.origin.x -= offset.x;
 fakeBounds.origin.y -= offset.y;
 fakeBounds.size.width += offset.x;
 fakeBounds.size.height += offset.x;
 NSSize fakeSize = fakeBounds.size;
 NSBitmapImageRep *bir = [self bitmapImageRepForCachingDisplayInRect:fakeBounds];
 [bir setSize:fakeSize];
 [self cacheDisplayInRect:fakeBounds toBitmapImageRep:bir];
 NSImage* image = [[[NSImage alloc] initWithSize:realSize] autorelease];
 [image lockFocus];
 verify( [bir drawAtPoint:fakeBounds.origin] );
 [image unlockFocus];
 return image;

Posted Tuesday, June 23, 2009 05:38 AM by Peter N Lewis.

Post Comment

Name: (optional)
URL: (optional)
Email: (optional, not published)
CAPTCHA: eight one five four (required)
  To prove you are human, please enter the number as digits (ie, 1-9).
  You can use some HTML tags such as <b>, <i>, and <a href="">.
We reserve the right to remove any offensive or inappropriate comments.
Due to spam issues, comments are initially invisible until we review them,
you can see them (background red) and we can see them, but no one else.

Comment Preview

None yet.

Buy Now

User Database