27

I'm tired of taking new screenshots everytime I change my UI for my iPhone application. I would like to be able to run a script/program/whatever to load my binary on the simulator and then take a few screenshots.

The solution can be in any language... it doesn't matter to me.

Thanks!

JP Richardson
  • 38,609
  • 36
  • 119
  • 151

6 Answers6

25

With iPhone SDK 4 you can automate GUI tests, and it can take screenshots for you.

Basically, you write a Javascript script, and then Instruments (using Automation template) can run it on the device to test the UI, and can Log data, screenshot, etc., and also can alert if something's broken.

I couldn't find a reference guide for it, but in the SDK reference library search for UIA* classes (like UIAElement).

There's also a video demoing this from WWDC, session 306.

mohsenr
  • 7,235
  • 3
  • 28
  • 28
  • 1
    Here is the reference documentation from Apple: https://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/Built-InInstruments/Built-InInstruments.html#//apple_ref/doc/uid/TP40004652-CH6-SW75 – Jon Tirsen Dec 13 '11 at 20:34
12

I have the same wish. I want to be able to save screenshots from a few screens in my app without all the manual work. I'm not there yet, but I have started.

The idea is to tail /var/log/system.log, where the output from the NSLog statements go. I pipe the output to a python program. The python program reads all the lines from stdin and when the line matches a specific pattern, it calls screencapture.

NSLog(@"screenshot mainmenu.png");

That will cause a screenshot named "XX. mainmenu YY.png" to be created every time it is called. XX is the number of the screenshot since the program started. YY is the number of the "mainmenu" screenshot.

I have even added some unnecessary features:

NSLog(@"screenshot -once mainmenu.png");

This will only save "XX. mainmenu.png" once.

NSLog(@"screenshot -T 4 mainmenu.png");

This will make the screenshot after a delay of 4 seconds.

After running an app with the right logging, screenshots with the following names could have been created:

00. SplashScreen.png
01. MainMenu 01.png
03. StartLevel 01.png
04. GameOver 01.png
05. MainMenu 02.png

Give it a try:

  1. Add some NSLog statements to your code

  2. $ tail -f -n0 /var/log/system.log | ./grab.py

  3. Start your iPhone app in the Simulator

  4. Play around with your app

  5. Have a look at the screenshots showing up where you started the grab.py program

grab.py:

#!/usr/bin/python

import re
import os
from collections import defaultdict

def screenshot(filename, select_window=False, delay_s=0):
    flags = []
    if select_window:
        flags.append('-w')
    if delay_s:
        flags.append('-T %d' % delay_s)
    command_line = 'screencapture %s "%s"' % (' '.join(flags), filename)
    #print command_line
    os.system(command_line)

def handle_line(line, count=defaultdict(int)):
    params = parse_line(line)
    if params:
        filebase, fileextension, once, delay_s = params
        if once and count[filebase] == 1:
            print 'Skipping taking %s screenshot, already done once' % filebase
        else:
            count[filebase] += 1
            number = count[filebase]
            count[None] += 1
            global_count = count[None]
            file_count_string = (' %02d' % number) if not once else ''

            filename = '%02d. %s%s.%s' % (global_count, filebase, file_count_string, fileextension)
            print 'Taking screenshot: %s%s' % (filename, '' if delay_s == 0 else (' in %d seconds' % delay_s))
            screenshot(filename, select_window=False, delay_s=delay_s)

def parse_line(line):
    expression = r'.*screenshot\s*(?P<once>-once)?\s*(-delay\s*(?P<delay_s>\d+))?\s*(?P<filebase>\w+)?.?(?P<fileextension>\w+)?'
    m = re.match(expression, line)
    if m:
        params = m.groupdict()
        #print params
        filebase = params['filebase'] or 'screenshot'
        fileextension = params['fileextension'] or 'png'
        once = params['once'] is not None
        delay_s = int(params['delay_s'] or 0)
        return filebase, fileextension, once, delay_s
    else:
        #print 'Ignore: %s' % line
        return None

def main():
    try:
        while True:
            handle_line(raw_input())
    except (EOFError, KeyboardInterrupt):
        pass

if __name__ == '__main__':
    main()

Issues with this version:

If you want to take a screenshot of only the iPhone Simulator window, you have to click the iPhone Simulator window for every screenshot. screencapture refuses to capture single windows unless you are willing to interact with it, a strange design decision for a command line tool.

Update: Now iPhone simulator cropper (at http://www.curioustimes.de/iphonesimulatorcropper/index.html) works from command line. So instead of using the built in screencapture, download and use it instead. So now the process is completely automatic.

Offe
  • 184
  • 5
  • 2
    One major problem with this technique is that it requires a hires screen to work. I do my development on a 15" MBP without an external monitor, and this tool can't capture iPad-sized screenshots. Any advice on ways to automate this without a bigger screen? – radven Dec 20 '10 at 18:32
7

Inside the iPhone Simulator, there is a "Copy Screen" menu item. It replaces the Copy menu item in the edit menu when you hold down Control. The keystroke is Ctrl-Cmd-C A simple AppleScript could copy a ScreenShot and save it. Something like (it worked for me, even if it is hacky):

tell application "iPhone Simulator" to activate
tell application "System Events"
    keystroke "c" using {command down, control down}
end tell
tell application "Preview" to activate
tell application "System Events"
    keystroke "n" using {command down}
    keystroke "w" using {command down}
    delay 1
    keystroke return
    delay 1
    keystroke "File Name"
    keystroke return
end tell

If you don't get it, please comment...

Tom H
  • 1,316
  • 14
  • 26
6

The private UIGetScreenImage(void) API can be used to capture the contents of the screen:

CGImageRef UIGetScreenImage();
void SaveScreenImage(NSString *path)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    CGImageRef cgImage = UIGetScreenImage();
    void *imageBytes = NULL;
    if (cgImage == NULL) {
        CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
        imageBytes = malloc(320 * 480 * 4);
        CGContextRef context = CGBitmapContextCreate(imageBytes, 320, 480, 8, 320 * 4, colorspace, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big);
        CGColorSpaceRelease(colorspace);
        for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
            CGRect bounds = [window bounds];
            CALayer *layer = [window layer];
            CGContextSaveGState(context);
            if ([layer contentsAreFlipped]) {
                CGContextTranslateCTM(context, 0.0f, bounds.size.height);
                CGContextScaleCTM(context, 1.0f, -1.0f);
            }
            [layer renderInContext:(CGContextRef)context];
            CGContextRestoreGState(context);
        }
        cgImage = CGBitmapContextCreateImage(context);
        CGContextRelease(context);
    }
    NSData *pngData = UIImagePNGRepresentation([UIImage imageWithCGImage:cgImage]);
    CGImageRelease(cgImage);
    if (imageBytes)
        free(imageBytes);
    [pngData writeToFile:path atomically:YES];
    [pool release];
}

Be sure to wrap it inside an #ifdef so it doesn't appear in the release build.

rpetrich
  • 32,196
  • 6
  • 66
  • 89
  • Seems that UIGetScreenImage no longer works on the simulator :( (last I tried was on 2.x). I have updated the code to include the basics of manual screen capturing... it seems to work pretty well with portrait windows, yet not so well with landscape and it doesn't include the statusbar; but it's a start. If you improve it please update my answer :) – rpetrich Sep 11 '09 at 06:54
1

If you are interested in automating changing the simulator language and the device type automatically, I developed some scripts that do that: http://github.com/toursprung/iOS-Screenshot-Automator

KrauseFx
  • 11,551
  • 7
  • 46
  • 53
0

You might also use some screen capturing application to capture a video on the Simulator's screen.

I use the Jing app a lot.

I even use it to send a video that presents the application to clients...

Michael Kessler
  • 14,245
  • 13
  • 50
  • 64