Developing for both retina and non-retina iOS screens using AIR 2.6

Posted on March 21, 2011 | 24 comments

AIR 2.6 provides updates to the old Packager for iPhone (PFI). The packager binary is now part of the normal AIR packaged called adt. The new adt options are just like the old pfi packager options, see below:

adt -package -target ( ipa-test | ipa-debug | ipa-app-store | ipa-ad-hoc ) 
CONNECT_OPTIONS? SIGNING_OPTIONS <output-package> ( FILE_OPTIONS | <input-package> )



Screen Resolution and Pixels Per Inch (ppi)

When developing you applications for retina and non-retina iOS screens, for example a iPhone 3GS and a iPhone 4G, you need to take in account the large differences in resolutions. The retina enabled devices have have a 960×640 resolution 3.5in physical screen which comes out to 324ppi . For the non-retina displays the screen is a 480×320 resolution 3.5in physical screen which comes out to 162ppi. AIR 2.6 allows for developers to set a mode in the application descriptor file which allows application to use the retina display if present on the device. This means an application needs to be able to render to both retina and non-retina displays. How do we do this?

Abstractly lets just talk about what this means in terms of pixels. You basically have two methods of tackling this in code: start with the lower resolutions and scale up or code for the higher resolution and scale down. In iOS native SDK things are represented in “points” and not pixels, so if you place an object at x,y of 15,15 it would know how to put it at 30,30 in a retina display screen. But the issue then comes with images or non-vector/drawing commands. In Objective C they provide a mechanism to save a 2nd image with a specific name pattern to be use on retina enabled devices, for example my.png and my@2x.png would cover both non-retina and retina displays.

What about in the AIR based applications?

In Flash we still play with pixels, so turning on the option for retina display will make your SWF either 960×640 or 480×320 in size depending on retina support. This means as a developer you can check the Capabilities.screenDPI, Capabilities.screenResolutionX, and Capabilities.screenResolutionY to find out what ppi and resolution the device is. All the mouse events will come back in the 960×640 space in retina display, but this is not a factor if you are attaching mouse events onto DisplayObjects, if not just use a factor based on the screenResolutionX/Y.

How to design for this? There is a couple of options: two apps in one SWF (at the two resolutions) or some kind of scaling. The first option doesn’t make much sense for the retina display case. In the case of scaling applications Flash does have a few options discussed below.

Scaling Approach #1 – PickQuick Example

A good example of this is the PickQuick application I built to work across smartphones, tablets, and TV screens (see the video here). In this application I designed the layout to flow based on screen resolution and size. I started with largest screen size by screen resolution and physical size, the iPad at 1024×768. I created a background that was 768 pixels high and wide enough for a pattern I could draw across the back to fill up the various screen widths. The header also has a background that was repeated with the same approach. The other images that make up the backgrounds for the TextField displays numbers. These square images and TextFields are a specific size and laid out based on the dimensions of the devices.

Its also important to note that PickQuick has both the stage‘s scaleMode and align set so it won’t scale and be positioned relative to the top left of the screen. This is done in ActionScript by setting the following code:

stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;

I’ll show in the next approach how to use the stage’s scaleMode to handle scaling for you. Now back to PickQuick.

The one case where this doesn’t look good with out scaling is on the iOS devices of 480×320 – 162ppi specs. In this case I check for this low resolution smaller screen device and then scale the images by a scale factor, I chose 0.6 (or 60% smaller). I apply the scale factor to the images before drawing and to the TextField’s font size. Applying the scale to the TextField’s font size directly makes for readable text, compared to scaling a Embedded font or bitmap image of the text. Since the original design for PickQuick was a high resolution by default it worked well with the new AIR 2.6 retina display option and I didn’t have to change any code in my application. But I did have to package the application for the retina display. I’ll explain how to do that now. All the code can be found on my github project called PickQuick.

First thing to do is add <requestedDisplayResolution>high</requestedDisplayResolution> to your application’s descriptor file. The next thing is to add a high resolution icon for the retina display screen, this is done by defining an <image114x114>icon114.png</image114x114> in the <icon> attribute. Here is the full application descriptor file (PickQuick-app_iOS.xml) that I used to package up PickQuick for all iOS devices (iPhone/iPod/iPad both retina and non-retina devices):

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://ns.adobe.com/air/application/2.6">
<id>com.renaun.PickQuick</id>
<filename>PickQuick</filename>
<name>PickQuick</name>
<versionNumber>1.0.3</versionNumber>
<initialWindow>
    <content>PickQuick.swf</content>
    <visible>true</visible>
    <fullScreen>true</fullScreen>
    <aspectRatio>landscape</aspectRatio>
</initialWindow>
<supportedProfiles>mobileDevice desktop</supportedProfiles>
<icon>
    <image29x29>icon29.png</image29x29>
    <image57x57>icon57.png</image57x57>
    <image114x114>icon114.png</image114x114>
</icon>

<iPhone>
  <InfoAdditions>
  <![CDATA[
   <key>UIDeviceFamily</key>
   <array>
   <string>2</string>
    <string>1</string>
   </array>
 ]]>
  </InfoAdditions>
  <requestedDisplayResolution>high</requestedDisplayResolution>
  </iPhone>
</application>

Now you just need to package it up with AIR 2.6 and you are good to go. Here is the adt command line that I used:

AIR26/bin/adt -package -target ipa-test -storetype pkcs12 -keystore renaun_cert.p12 
-storepass password -provisioning-profile RenaunDev.mobileprovision 
PickQuick.ipa PickQuick-app_iOS.xml PickQuick.swf Default.png icon*


Scaling Approach #2 – Stage’s ScaleMode

By default each SWF has a stage with a scaleMode set to StageScaleMode.SHOW_ALL. This will auto scale the SWF to fit into either the width or height of the screen. Which is a bit easier then checking the Capabilities properties to figure out what resolution the application is in. In this case for retina display if you create a 960×640 SWF, it will force the SWF to scale down to 480×320. This is effectively the same thing as doing this on a 960×640 SWF:

stage.scaleMode = StageScaleMode.NO_SCALE;
this.scaleX = 0.5;
this.scaleY = 0.5;

Now this works cleanly because of the clean ratio between 960×640 and 480×320. But there is some things to consider. You have to design for the scale factor by 50%, pay attention to text and other design assets. This approach works nicely as you don’t have to scale by code any assets, letting you not worry about more complex bitmap draw commands or applying scaleX/Y to a bunch of DisplayObjects.

Other Tips

Performance at the 960×640 resolution will be worse then at 480×320, this is because it is not just twice the pixels but four times the pixels to render. So spend time optimizing and testing first on a device with the 960×480 retina display.

  • Pingback: MadeInFlex » Blog Archive » AIR 2.6 released!!

  • Robert

    Hi Renaun,
    Nice work once again. A question to this iPhone packager. I’m trying to work out with producing my ipa files from both flash builder or flash ide but I don’t seem to have it applied I think as the performance is still the same. I’ve installed with this instruction http://www.laaker.com/micah/blog/2007/installing-the-adobe-air-sdk-on-a-mac

    on first install, when i do echo $PATH, I got the right result with AIRSDK and all that also when typed adl works fine. I saved .profile and .bashrc. But when terminal is closed adl command isn’t found. Any idea?

    And with this adt command line that you used:

    AIR26/bin/adt -package -target ipa-test -storetype pkcs12 -keystore renaun_cert.p12
    -storepass password -provisioning-profile RenaunDev.mobileprovision
    PickQuick.ipa PickQuick-app_iOS.xml PickQuick.swf Default.png icon*

    Is that from terminal? Is there any drag and drop solution or an app to do this? Seem so cumbersome that it doesn’t get delegated fully to flash ide/flash builder?

    Thanks in advance.

    • http://www.renaun.com Renaun Erickson

      That is from the command line. Both Flash Builder and Flash Pro tooling updates will come later.
      You should be able to do “adt -version” on the command line an confirm its 2.6

  • Robert

    Right.

    - So it’s done from Terminal only in Mac?
    - Do you have ETA when the integration for Flash IDE/Flash Builder?
    - Do you know why my adt command isn’t found now that I’ve tried installing twice but got replaced? Now my echo $PATH output is Library/Application Support/GoodSync:/Library/Frameworks/Python.framework/Versions/Current/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin

    You advice will be greatly appreciated. Thanks.

    • Robert

      I just tested it again by using these command lines:
      pico .profile
      pico .bashrc

      making sure that all export path are there (and it’s there). and did this:
      . .profile
      . .bashrc

      then i typed adl then it works. Any idea of why it doesnt work when i close the terminal and had to re-do them all? is that how it’s spose to be done?

  • Pingback: Daily Developer Journal – March 22, 2011

  • http://www.26oclock.com Scott Rapson

    Hi, Thanks for posting this! All seems fine except for this bizarre error which is thrown…

    The command executes and the little java thing starts, waits for a second, then posts this to the command line.

    NotAfter: Thu Mar 03 15:43:32 CST 2011

    I tried changing the date and time to before that, thinking it was a time date issue, but to no avail.

    Any ideas? Thanks

    • http://www.renaun.com Renaun Erickson

      I haven’t seen that error. I do see errors when my Java JRE on mac is messed up. What OS and type of app (Flash Pro, Flex, AS3 only)?

      • http://www.26oclock.com Scott Rapson

        Mac OSX 10.6, MacPro 09, and I get the errors from my Flash CS5, Flex and FlashDevelop based projects that I know have worked from the command line before.

    • SamLee

      Hi,

      icon114.png

      In my case,
      That element causes ADT Compile Error(java.pointer.NullPointerException)

      • http://www.renaun.com Renaun Erickson

        Whats your app.xml look like for the icons node? Are you using AIR 2.6?

      • SamLee

        I’m sure that i’m using AIR2.6
        Here’s my app.xml
        ———————————-

        kr.dive.mobile.ios
        1.1.2
        iMARS
        iMARS

        iMARS.swf
        standard
        false
        true
        true
        portrait
        gpu
        false

        mobileDevice desktop

        AppIconsForPublish/Icon_29.png
        AppIconsForPublish/Icon_57.png

        AppIconsForPublish/Icon_512.png
        AppIconsForPublish/Icon_48.png
        AppIconsForPublish/Icon_72.png

        <![CDATA[UIDeviceFamily21]]>

        high

  • guillaume gaulard

    yahoo ! exactly what i was looking for.
    i love you blog each day a little more.

  • SamLee

    Hi Renaun,
    It is my honor to see your blog.

    There is a missing class in github project “PickQuick”.
    The src folder doesn’t contain “com.kaigames.core” classes.

    Could you please update it :)

    • http://www.renaun.com Renaun Erickson

      Those files are in my KaigamesLibrary github.

  • Drew

    This code does not seem to work.

    ACTIONSCRIPT:
    stage.scaleMode = StageScaleMode.NO_SCALE;
    stage.scaleX = 0.5;
    stage.scaleY = 0.5;

    I get this error:
    Error: Error #2071: The Stage class does not implement this property or method.
    at Error$/throwError()
    at flash.display::Stage/set scaleX()

    • http://www.renaun.com Renaun Erickson

      Thanks that should have been this.scaleX = 0.5; and this.scaleY = 0.5; on the root DisplayObject of your application.

  • djsuperfive

    Hi renaun,
    could you please update your post for a Flash Builder 4.5 project ? Is it still necessary to have several app descriptor files ?
    Thank you for your great post

    • http://www.renaun.com Renaun Erickson

      You do not need multiple application descriptor files, it was more of a way to show what each OS has so people see what goes with what.
      The github code doesn’t have project files so it should work just fine in FB 4.5, what project file are you referring to?

  • http://www.facebook.com/people/?????-??????/781590402 ????? ??????

    >Scaling Approach #2 – Stage’s ScaleMode

    Does this mean that I don’t have to scale all images manually?
    The whole stage will scale down to non retina display automatically?

    • Anonymous

      Yes, if you scale the stage all assets get scaled.

      • http://www.facebook.com/profile.php?id=692607174 Stephen Rose

        Do you know if there are any major performance implications with this? It seems as though we’re asking slower devices to perform extra work by scaling every bitmap before it can run all the usual stuff.

        • Anonymous

          No, its isn’t a burden the real performance issue is how many pixels have to be rendered, since Flash Player is rendering less pixels with it scaled it works nicely.

  • http://www.facebook.com/people/?????-??????/781590402 ????? ??????

    Thank you!

  • NoahGuest

    stage.scaleMode = StageScaleMode.NO_SCALE;
    this.scaleX = 0.5;
    this.scaleY = 0.5;
    I tried the above code and nothing at all happened.

    • Anonymous

      Depends on if u want the SWF to auto scale for u or if u want to handle all resolutions changes yourself. Look at stage.scaleMode