Saving a view as an image.
Another real quick tip for you. Depending on what you’re doing, why might want to capture a view and store it as an image. The frameworks make this quick and painless.
The first step you should take is to create a new image context in which we will be working. The next step is to render the view’s CALayer into that new context. Finally, we get an image from that context and close out the context.
In the following code, I use the UIWindow (UIView subclass) for the sample as a way to get a quick and easy screenshot.
- (void)takeScreenshot { UIWindow *theScreen = [[UIApplication sharedApplication].windows objectAtIndex:0]; UIGraphicsBeginImageContext(theScreen.frame.size); [[theScreen layer] renderInContext:UIGraphicsGetCurrentContext()]; UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); [self doSomethingWith:screenshot]; }
The main application window will always be the first window in your application’s windows array.
The above code should work flawlessly, although it will give you a warning. If you want to remove the warning, add the QuartzCore framework into your project and import its main header.
You may notice that the above method returns an “application shot” instead of a full screenshot. This is because we are only capturing the view and its subviews. This is ideal if you want to capture part of your application. However, if you want a full screenshot with the StatusBar and all, you will need to use a bit of undocumented API.
To get a screenshot, you will need to use the undocumented UIGetScreenImage() function call. Of course, this will create a warning, so go ahead and declare the method in your header.
CGImageRef UIGetScreenImage();
As you see, the UIGetScreenImage() returns a CGImageRef instead of a UIImage (as expected). Simply create a UIImage out of this CGImageRef, and you are done. The following code grabs a full screen screenshot in one line of code.
- (void)takeScreenshot { UIImage *screenshot = [UIImage imageWithCGImage:UIGetScreenImage()]; [self doSomethingWith:screenshot]; }
The UIGetScreenImage() function does not work on the Simulator.
I am attaching a simple project to this post that takes a screenshot using each method. I figured the easiest way to display the screenshot image for the purpose of the demonstration was to email it.
I create an instance of the mail controller and name it “email.” Bad naming, I know. I then set the subject as “Screenshot” and add the screenshot image we took as an attachment.
- (void)sendImage:(UIImage *)image { MFMailComposeViewController *email = [[MFMailComposeViewController alloc] init]; email.mailComposeDelegate = self; [email setSubject:@"Screenshot"]; [email addAttachmentData:UIImagePNGRepresentation(image) mimeType:@"image/png" fileName:@"Screenshot"]; [self presentModalViewController:email animated:YES]; [email release]; }
Update: Apple have now allowed the usage of UIGetScreenImage(). Keep in mind that you may use the method as of now to gather a screenshot, but don’t rely on it in your application. The announcement hints strongly that Apple will be adding a new public method into future frameworks that will gather a screenshot. Keep an eye on the changelogs between firmwares, and be ready to update your applications when the time comes.
Download link: Screenshot.zip
Comments
13 Comments on Saving a view as an image.
-
Andrew Ash on
Sat, 19th Dec 2009 11:58 pm
-
Skylar on
Sun, 20th Dec 2009 12:22 am
-
Andrew Ash on
Sun, 20th Dec 2009 12:56 pm
-
nglayton on
Sat, 9th Jan 2010 9:15 am
-
Skylar on
Thu, 14th Jan 2010 1:36 am
-
DanCreek on
Sun, 28th Mar 2010 5:31 pm
-
DaveF on
Wed, 4th Aug 2010 4:14 pm
-
Dave J on
Thu, 3rd Feb 2011 11:04 pm
-
Mark on
Fri, 18th Feb 2011 11:19 am
-
DNA APP on
Thu, 31st Mar 2011 9:01 am
-
Bhushan on
Wed, 8th Jun 2011 11:21 pm
-
Jeremy Fuller on
Sun, 11th Sep 2011 11:30 am
-
Guest on
Tue, 15th Nov 2011 7:14 am
I’ve been struggling with a memory leak in an iPhone app, and I think I’ve traced it down to using the takeScreenshot method you have above. The relevant line is this:
UIImage *screenshot = [UIImage imageWithCGImage:UIGetScreenImage()];
This line calls UIGetScreenImage(), which returns a CGImageRef, and uses that reference to create a UIImage. The problem I think is in what is done with the CGImageRef. It seems that UIGetScreenImage expects you to release the CGImageRef yourself, using CGRelease. Replacing the above line of code with the following lines fixed a massive memory leak I had:
CGImageRef screenshotCG = UIGetScreenImage();
UIImage *screenshot = [UIImage imageWithCGImage:screenshotCG];
CFRelease(screenshotCG);
Credit where credit is due — I had the idea to check this from a code snippet at:
http://netsharc.wordpress.com/2008/12/25/iphone-dev-creating-a-full-screen-camera-preview/
Thanks for the catch. That’s the problem with writing up a quick tutorial without testing it, you sometimes forget releases : )
In the future though, you might want to use CGImageRelease instead of CFRelease. According to Apple, the two are equivalent, except CGImageRelease won’t throw an error if the image is NULL.
[WORDPRESS HASHCASH] The poster sent us ‘0 which is not a hashcash value.
Thanks Skylar, CGImageRelease seems to be the way to go there.
did the source attached to the post get updated. I can’t tell, I don’t see any of the lines talked about in the source code. Was there a 3rd change not posted ?
Good catch, I have not updated the sample code attached to post. Honestly, this is such a simple post that I simply forgot that I had attached any downloadable source code. I will update the download link at some indeterminate date in the future.
Thanks!
[WORDPRESS HASHCASH] The poster sent us ‘0 which is not a hashcash value.
Thanks for the memory leak tip Andrew. You just saved me hours of troubleshooting.
Awesome snippet Skylar. One thing is that I’m using this code on an iPad application in landscape orientation and the resulting screengrab is in portrait mode. I know I could just alter the image itself but is there a cleaner way to get the image at the correct orientation?
Thank you for posting this tutorial. Very useful and clear!
Can you still use UIGetScreenImage() in 4.2?
Thanks a lot for the tutorial. It helped a lot especially you guilded how to capture a screenshot & use UIImagePNGRepresentation. Highly appreciate
I found a very minor items. You missed out to add a checking. Without that, it will fail to run on the phone which didnt setup the email.
if ( email == nil)
{
NSLog(@”Device not configured to send email”);
return;
}
Any ideas how I can implement this in Rhodes framework
If you’re trying to do this on a retina device, you’ll notice your view’s snapshot is captured at half resolution. To fix this, open the context using this instead:
UIGraphicsBeginImageContextWithOptions(gridView.frame.size, NO, 0);
The 0 at the end causes the context to be opened using the device’s correct scale.
Is all that code “fully app store approved”?
Also, how would I save the screen-shot picture to the camera roll?
Tell me what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!
