Screen Capture in iOS Apps

by Bob McCune on September 8, 2011

I occasionally come across the need to grab the contents of a view as an image. This is often the result of needing to perform some non-stock, animated transition between views, but there are a variety of reasons why this might be useful. Thanks to the Core Animation framework’s CALayer class, this is easy to do.

All UIView instances have an underlying instance of a CALayer. The layer is responsible for rendering the view’s contents and performing any view-related animations. CALayer defines a method called renderInContext: which allows you to render the layer, and its sublayers, into a given graphics context:

UIGraphicsContext context = // some graphics context
[viewToCapture.layer renderInContext:context];

Before you can access any layer-specific APIs, you’ll need to make sure you’re linking against the QuartzCore framework. Xcode’s default templates don’t link against this framework so you’ll need to select Target Name > Build Phases > Link Binary With Libraries and select QuartzCore.framework.

Additionally, you’ll need to add the following import to your code wherever you are calling the layer’s properties or methods:

#import <QuartzCore/QuartzCore.h>

With the necessary project configuration out of the way, the next question is where do we get a graphics context into which we can render the view’s content? This can be created using UIKit’s UIGraphicsBeginImageContextWithOptions function which will create a new bitmap-based graphics context for us.

UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale);

This function takes a CGSize (you view’s size), a BOOL indicating if your view is opaque, and a CGFloat specifying the scaling factor. If you’re rendering a fully opaque, rectangular view you can pass YES for the opaque argument so the alpha channel can be discarded from the context.

Now that we’ve created a graphics context we can use the UIGraphicsGetCurrentContext() and UIGraphicsGetImageFromCurrentImageContext() functions to get reference to this new context and retrieve the rendered image data from it. Finally, we’ll want to call the UIGraphicsEndImageContext() function to clean up this context and remove it from the stack. Putting all this together we end up with the following:

// Render the views layer contents into the current Graphics context
CGSize viewSize = viewToCapture.bounds.size;
UIGraphicsBeginImageContextWithOptions(viewSize, NO, 1.0);
[viewToCapture.layer renderInContext:UIGraphicsGetCurrentContext()];
// Read the UIImage object
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

To see this code in action I’ve put together a simple demo app. You can tap on Archer, Lana, or the background to capture the contents of the view and write the image to your Photo Library.

Note: Before running the demo be sure to open the “Photos” app on the Simulator so it can initialize its database or the images won’t be written. Enjoy!

Download Demo

[ 3 comments… read them below or add one ]

Mark September 8, 2011 at 2:59 am

Maye I’m missing something, but how is this different than just pressing Power+Home buttons and capturing the screen to the camera roll?

Reply

Bob McCune September 8, 2011 at 3:05 am

Yes, you’re missing the point. Power+Home is a handy user feature, but doesn’t do anything for a developer. The intent of this post is to explain how you can capture a view (or any subview’s content) so you can use image data within your app.

Reply

Abraham Ventura October 5, 2011 at 3:53 pm

This was a great tutorial.. The best that I have seen that captures the screen.. Thanks.. and thanks for making it fun.. :) I give it a A+ and you got me as a fan.

Reply

Leave a Comment