5 Ways to Take Screenshots of Your iPhone App

July 30th, 2009 by Derek van Vliet Leave a reply »

Screenshots are an integral tool in application development, not to mention marketing. You may find you need a screenshot in any number of situations. Here are 5 ways you can capture a screenshot of your iPhone app. Each one is useful in a different way.

Do It With OS X

The first method is good old screen capture that is built into OS X. This is, of course, not exclusive to iPhone apps. You can hold down Command-Shift-3 to save a screenshot to your desktop in PNG format. This is useful for if you want to take a screenshot while you are running your app in the emulator.

Even more useful, in my opinion, is the Command-Shift-4 function. This turns your cursor into cross-hairs and lets you select a region of the screen to save as your screenshot, again as a PNG. This is great if you just wanted to grab a portion of your app.

You can also add Ctrl to both of the above keyboard shortcuts to save the screenshot to your clipboard instead of to disk. Then you can go paste the screenshot into Photoshop or some other image editor.

Do It With Grab

Grab is an application that comes with OS X which you can find in your Applications/Utilities folder. It features the unique ability to take a screenshot of a window:

grab-menu

This means if you run your app in the iPhone Simulator and then use Grab, you can take screenshots of your app looking all hot in the actual device with just a couple of clicks. This is a great way to get shots of your app in the iPhone if you don’t own one or aren’t a paid developer. Here’s the result:

grab

Screenshots that come out of Grab are in TIFF format.

Do It With Xcode

Xcode’s Organizer tool provides a simple interface for taking screenshots of whatever is currently displaying on an iPhone that is connected to your computer. Open it by going to Xcode’s Window menu and selecting Organizer or just hit Ctrl-Command-O.

organizer

Once it is opened, select your iPhone from the Devices section on the left. Then go to the Screenshot tab in the main section. Now use your application on the iPhone until it is in a state where you want to take a screenshot. Finally, press the capture button in Organizer and boom: you’re done.

Xcode saves screenshots as PNG files in the following folder on your machine:

~/Library/Application Support/Developer/Shared/Xcode/Screenshots/

Do It With the iPhone

Did you know you can take a screenshot of anything that the iPhone is doing at any time? It’s true. Just press and hold the home button. While you’re holding it, press and release the Sleep/Wake button. The iPhone’s screen will flash and the camera shutter sound will play. The screenshot will have been saved to your camera roll, which you can access in the Camera app or the Photos app.

iphone

Do It With Code

Finally, you can capture screenshots at precise moments in your application by using the following pair of objective-c methods. The first one, captureView, returns a UIImage that contains a render of any UIView (except EAGLView, see below). If you pass your app’s UIWindow into this method, it will return you a screenshot of the app.

The second method, saveScreenshotToPhotosAlbum, takes it a step further and saves an image that contains a render of any UIView to your iPhone’s photo album.

#import <QuartzCore/QuartzCore.h>
 
- (UIImage*)captureView:(UIView *)view {
	CGRect rect = [[UIScreen mainScreen] bounds];	 
	UIGraphicsBeginImageContext(rect.size);	 
	CGContextRef context = UIGraphicsGetCurrentContext();	 
	[view.layer renderInContext:context];	 
	UIImage *img = UIGraphicsGetImageFromCurrentImageContext();	 
	UIGraphicsEndImageContext();	 
	return img;
}
 
- (void)saveScreenshotToPhotosAlbum:(UIView *)view {
	UIImageWriteToSavedPhotosAlbum([self captureView:view], nil, nil, nil);
}

Since the above 2 methods don’t work for EAGLView, we need another solution. Below are a pair of methods that do the equivalent of the above 2 for apps using EAGLView, courtesy iPhone Dev SDK Forum.

- (UIImage*) getGLScreenshot {
    NSInteger myDataLength = 320 * 480 * 4;
 
    // allocate array and read pixels into it.
    GLubyte *buffer = (GLubyte *) malloc(myDataLength);
    glReadPixels(0, 0, 320, 480, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
 
    // gl renders "upside down" so swap top to bottom into new array.
    // there's gotta be a better way, but this works.
    GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
    for(int y = 0; y <480; y++)
    {
        for(int x = 0; x <320 * 4; x++)
        {
            buffer2[(479 - y) * 320 * 4 + x] = buffer[y * 4 * 320 + x];
        }
    }
 
    // make data provider with data.
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL);
 
    // prep the ingredients
    int bitsPerComponent = 8;
    int bitsPerPixel = 32;
    int bytesPerRow = 4 * 320;
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
 
    // make the cgimage
    CGImageRef imageRef = CGImageCreate(320, 480, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
 
    // then make the uiimage from that
    UIImage *myImage = [UIImage imageWithCGImage:imageRef];
    return myImage;
}
 
- (void)saveGLScreenshotToPhotosAlbum {
	UIImageWriteToSavedPhotosAlbum([self getGLScreenshot], nil, nil, nil);	
}

  • David Casseres
    Thanks, your captureView: is just what I needed. Well, almost. I need to capture not
    the whole screen, but a rectangular portion of it, so I wrote this:

    - (UIImage*)captureView:(UIView *)view bounds:(CGRect)rect
    {
    UIGraphicsBeginImageContext(CGSizeMake(320, 480));
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextClipToRect(context, rect);
    [view.layer renderInContext:context];
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return img;
    }

    which works just fine.
  • Jack
    Nice article, you can also add this tool to the list, which specifically captures the simulator:
    http://www.curioustimes.de/iphonesimulatorcropp...
  • moldie
    Great article, works realy well. What I can't work out is how to access the iPhone screen from my app so I can make a png in it so it appears that the app is transparent? Any ideas?
  • Hi moldie, I'm having trouble understanding what you are attempting to do. If I read correctly, you're trying to take a screen capture of your app at run time? If so you can accomplish this with the above code. I don't understand what you mean by "so it appears that the app is transparent." Could you please elaborate?
  • moldie
    Hi, what I was trying to do was capture the screen 'before' the app launch so I could then use the png to play around with it in my app - like for fun say load the app in and "appear" to rub out the phone icons with my finger. Any of this making sense? I tried loading a transparent view then using the code but it was blank.
  • I understand now, thanks for clarifying!

    From tests that I have run, it looks like the earliest opportunity to run code happens after the iPhone has cleared away the homescreen and launched your app's splash screen. I'm not sure it's possible to take a screenshot of the icons on the homescreen before your app runs, but if there's a chance, you'll want to see if you can take a screenshot in the entry point function of the app (eg. main).
  • Bart Jaques
    I'm trying to capture a small portion of a MKMapView. I have an annotation view that lies overtop.

    If I pass in the annotation view, all I get is what I draw myself in the annotation view, and nothing from the MKMapView background. Even if I draw nothing and the annotation view is 100% transparent and empty, nothing shows up from the MKMapView.

    If I pass in the MKMapView, I capture the whole map.

    If I pass in the MKMapView but use CGContextClipToRect with the small view's frame or bounds, I get a big image the size of MKMapView, empty but for the clipping rectangle.

    If I use UIGraphicsBeginImageContext to make a smaller context matching the small view's size, I only capture the top-left portion of the MKMapView, not the part I want. And calling CGContextClipToRect with the small view's frame just produces an empty image.

    Will I have to capture the full map, convert to a bitmap, and extract from that? Is there some other way to do this?
blog comments powered by Disqus