Snapshotting a view on iOS

Guides | Tutorial By 5 years ago

Snapshotting can be used on iOS to draw a view into an image, like a screenshot. Doing so is very beneficial in some scenarios such as smooth interface rotations and advanced animations.

Creating a snapshot is done via a CGContext with the following code:

//	Begin the image context
UIGraphicsBeginImageContextWithOptions(view.frame.size,	//	same size as our view
					NO,		//	transparent background (!opaque)
					0);		//	screen scale, 0 = main screen scale
CGContextRef ctx = UIGraphicsGetCurrentContext();

//	Translate to our active region, for example, where the content currently is in a scroll view
CGContextTranslateCTM(ctx, -view.bounds.origin.x, -view.bounds.origin.y);

//	Draw the view's layer
[view.layer renderInContext:ctx];

//	Grab the image
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();

//	End the image context (and clean up)
UIGraphicsEndImageContext();

Easy enough. Typically this should only be done on the main thread; if the main thread is drawing your views to the screen and one of your threads is drawing views to an image there could be some clashes. Having said this, I have found it safe to do this on a thread under the condition that it is done before the run loop ends (but test this thoroughly). For example if you needed to make a lot of these snapshots, you could spawn X threads and draw them asynchronously and then halt the main thread until all the drawing was finished. Note that if you ever do drawing on a thread, instead of using UIGraphicsGetCurrentContext you must create your own context.

In iOS7 this got even easier. There were 3 methods added to the UIView interface that will create snapshots faster than the older renderInContext: method.

If you simply want to visually create a copy then show the copy on screen, you can spawn a ‘replica’ static snapshot view, and set it’s frame and add it as a subview as you would any other view. This is done with the snapshotViewAfterScreenUpdates: method.

UIView* viewToCopy = ...;

UIView* snapshotView = [viewToCopy snapshotViewAfterScreenUpdates:YES];
[self.view addSubview:snapshotView];

There’s a similar method which instead creates a resizable snapshot, via resizableSnapshotViewFromRect:afterScreenUpdates:withCapInsets:.

If you need a UIImage version like in the above CGContextRef example, simply replace the [view.layer renderInContext:ctx]; line with the following:

//	Draw the view's layer
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];

So with this knowledge and a bit of work, you can create animations such as the springboard split folder animation:

  • James Bush

    Tip: change the name of the UIView view in the first block of sample code to snapshotView or viewToCopy, whichever it may be (I haven’t figured that out yet…).

    • James Bush

      One more thing: I got it to work with this…

      – (UIImage *)snapshotImage:(UIView *)view
      {
      UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, 0);
      [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
      UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();

      return image;
      }