/dev/trouble
Eric Roller's blog

Sunday, November 22, 2015

UIATarget.captureScreenWithName() producing corrupt images?

I like Jonathan Penn’s screen shooter scripts to automate the recording of screen shots for different iOS devices (screen sizes) as well as for multiple localisations (languages). Having said that, creating the automation scripts can be a pain and maintaining them with every change of the UI can be a real nuisance. …Breathe In… Sigh!

The trouble that I am addressing today, however, is about the resulting images. In my scripts, the screen shots are generated by calling either UIATarget.captureScreenWithName() or its sister function: UIATarget.captureRectWithName(). Sometimes, the images can look as if only partial, corrupted screens have been rendered.

Here is an example of a bad screen shot. You can see that most of the screen is missing. Note that I reduced the image to 25% of its size and added a border to highlight the issue:

bad_screen_shot.png

So, what is going on?

I am not sure who is to blame: iOS Simulater, Instruments (whose Automation plugin is used to drive the UI), or the timing of my scripts, which may interrupt some of the simulator’s rendering methods.

Anyway, for now, I got the problem to disappear after setting the view size to 100% in the simulator: Window > Scale > 100%. Also, you will have to set this for each of the devices for which you take screen shots!

While some of the simulated devices are far too large to be seen properly on a small screen, the captured images are complete and fully rendered!


P.S.

BTW, UIATarget.captureRectWithName() is a handy function to capture the screen without the title bar:

var target = UIATarget.localTarget();
var rect = target.rect();
target.captureRectWithName({origin:{x:0.0,y:20.0}, size:{width:rect.size.width, height:rect.size.height-20.0}}, "screen3");

Update 2: Automatically Resetting the View Scale

When I wrote “the simulator remembers the last setting” (below), I realised that the solution is to change the preferences. So, to have the view scale to be reset to 100%, automatically, I added this to _reset_all_sims within ui_screen_shooter.sh:

  # Reset all device window zoom scales to "100%"
  for device in "${simulators[@]}"; do
    # Convert "iPhone 6 Plus (9.1)" -> "iPhone-6-Plus"
    key=$(echo $device | sed 's/ (.*//; s/ /-/g;')
    key="SimulatorWindowLastScale-com.apple.CoreSimulator.SimDeviceType.$key"

    # Check if the key exists.
    scale=$(defaults read com.apple.iphonesimulator $key)
    if [[ $? == 0 && $scale != 1.0 ]]; then
      echo "Resetting Simulator scale for: $device"
      defaults write com.apple.iphonesimulator $key 1.0
    fi
  done

Update 1: Script to set View Scale

Don’t use this; use update 2 (above).

A quick search on the Internet pointed to a solution to use AppleScript to select the view scale in the simulator. While this probably works, I chose to use a simpler solution, namely to use an AppleScript to type “Cmd-1” (the menu shortcut for setting the scale to 100%).

function _resize_sim_window {
  osascript -e 'tell application "Simulator" to activate'
  osascript -e 'tell application "System Events" to keystroke "1" using command down'
}

N.B. Depending on your version of Xcode, the simulator may be called “iPhone Simulator”, “iOS Simulator”, or just “Simulator”.

One final hurdle remains: Since this needs to happen after Instruments has opened the simulator with the selected device, it is necessary to run Instruments twice. Therefore, in ui_screen_shooter.sh, where unix_instruments.sh is called, I inserted:

  if (( $do_resize )); then
    # Launch the simulator with an empty automation script.
    touch $trace_results_dir/nop.js
    "$DIR"/unix_instruments.sh 
      -w "$simulator" 
      -D "$trace_results_dir/nop" 
      -t "$tracetemplate" 
      "$bundle_dir" 
      -e UIASCRIPT "$trace_results_dir/nop.js" 
      -AppleLanguages "($language)" 
      -AppleLocale "$language"

    _resize_sim_window
  fi

  # Run the automation script.
  until "$DIR"/unix_instruments.sh 
    ...

Since _resize_sim_window activates the simulator, it can be difficult to use the computer at the same time. Hence the $do_resize variable which allows us to skip the resizing step. Typically you only need it once at the beginning or whenever you had changed the view sizes (do_resize=1). For subsequent runs you can skip it (do_resize=0) since the simulator remembers the last setting.