Making content look good on iPhone 4

The iPhone 4 screen has a significantly higher pixel density than the previous iPhone. Four times as many pixels on a screen of the same physical dimensions. The result is crisp and clear images with no pixel artifacts visible to the naked eye.

That is only true if the image displayed is at the native retina display resolution. If you’re viewing content created for a lower resolution screen the pixel edges become crisp and clear, so the content may actually look worse.

Picklet content, for example, authored for the original iPhone screen resolution looked wrong. All the text and gradients in the native app were clean and precise, but the picklet content, effectively a scaled-up set of low resolution images looked out of place.

Picklets should look insanely great on the retina display, still run on first-generation iPhone and iPod touch and the author shouldn’t have to handle the two versions explicitly, except for testing.

Creating two versions of each picklet

So. Images are created in Photoshop at the full resolution, and the exporter saves out two sets of images; one suitable for the retina display (at 640×960) and a second set scaled down (to 320×480) for the original iPhone display.

The reason for keeping a low resolution set of images for the ‘old’ iPhone is for performance reasons; one of the goals for the platform is that such simple content should be readable on first-gen iPhone and iPod touch devices.

One advantage of keeping a low resolution version is that it simplifies the layout of the picklet builder web interface. The small version is just easier to work with.

A side-product of authoring at retina resolution is that the resulting picklet looks good on iPad. The screen of the iPad has a slightly higher resolution than a retina iPhone, resulting in a 64px border left and right, and 32px top and bottom.

Handling the two content types on three devices

There are two sizes of picklet; call them original and fullsize. Original content gets displayed on original iPhone and the fullsize content is displayed for retina iPhone (and iPod touch) and iPad.

Each picklet must be tested on each device (which means loading it into MobileSafari), and ultimately deployed to the Picklets app where it is rendered in a UIWebView.

Some technical detail is required regarding the representation of picklet content. The picklet is stored as JSON data and is provided in the html on a hidden input element. It might look something like this (simplified for clarity);

{
  "about": "Short description of this picklet",
  "title": "This Picklet",
  "panels": [
    {
      "layers": [
        {
          "image": "/picklet/retina/panel_1/layer1.png",
          "name": "layer1.png",
          "origin_x_rel": 50,
          "origin_y_rel": 50,
          "start_x": 219,
          "start_y": 175,
          "end_x": 105,
          "end_y": 227
        },
        {
          "image": "/picklet/retina/panel_1/thumb.png",
          "name": "thumb.png",
          "origin_x_rel": 50,
          "origin_y_rel": 50,
          "start_x": 260,
          "start_y": 435,
          "end_x": 60,
          "end_y": 435
        },
        {
          // ... more layers
        }
      ]
    },
    {
      // ... more panels
    }
  ]
  // ... other properties of the picklet
}

The original resolution content is the value of a hidden input element with the id="id_content", something like this;

<input type="hidden"
  name="content"
  value="{/* JSON content appears here */}"
  id="id_content">

The picklet content for the retina version, which is a separate JSON object with different image urls and positions based on the larger screen size, is present in a different hidden input element;

<input type="hidden"
  name="retina_content"
  value="{}"
  id="id_retina_content">

When the dom loads, we determine which version to display;

if (window.navigator.fullsize == 1) {
  PickletViewer.init({
    'retina': true,
    'content_name': 'id_retina_content'
  });
} else {
  PickletViewer.init({
    'retina': false,
    'content_name': 'id_content'
  });
}

The window.navigator.fullsize is set when the page is loaded into MobileSafari. (Using a weird css media selector hack which I don’t show here.) When running in a UIWebView within the Picklets app it is explicitly injected based on the device’s screen dimensions;

-(void)viewDidLoad
{
  [super viewDidLoad];
  // self.playerView is the UIWebView component  
 
  UIScreen *mainScreen = [UIScreen mainScreen];
  UIScreenMode *screenMode = [mainScreen currentMode];
  CGSize size = [screenMode size];
 
  if (size.width == 320.0) {
    // original iPhone/iPod touch
    self.playerView = [[UIWebView alloc]
      initWithFrame:CGRectMake(0, 0, size.width, size.height)];
    [playerView stringByEvaluatingJavaScriptFromString:@"window.navigator.fullsize=0"];
  } else if (size.width == 640.0) {
    // retina display iPhone/iPod touch
    self.playerView = [[UIWebView alloc]
      initWithFrame:CGRectMake(0, 0, 320.0, 480.0)];
    [playerView
      stringByEvaluatingJavaScriptFromString:@"window.navigator.fullsize=1"];
    [playerView setScalesPageToFit:TRUE];
  } else {
    // iPad display or other
    self.playerView = [[UIWebView alloc]
      initWithFrame:CGRectMake(64.0, 32.0, size.width, size.height)];
    [playerView
      stringByEvaluatingJavaScriptFromString:@"window.navigator.fullsize=1"];
    [playerView setScalesPageToFit:TRUE];
  }
  // ...
}

The screen characteristics of the device are used to determine whether to display the ‘fullsize’ picklet content. This is communicated to the javascript interpreter using [UIWebView stringByEvaluatingJavaScriptFromString:NSString *]

For the retina version a UIWebView is created with dimensions of 320×480, but retina resolution content is scaled down to fit the smaller dimensions. The other part of this solution involves setting the document’s viewport to use the full device width and disable pinch or double-tap gesture to zoom content;

<meta name="viewport"
  content="width=device-width, user-scalable=no" />

The end result is a clean, crisp rendering of picklet content on retina iPhone and near full-screen display on iPad. And happy authors.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>