Developing for both retina and non-retina iOS screens using AIR 2.6
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 email@example.com 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.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
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
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.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):
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:
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.
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.