How to re-use the launch image in the app

Or: How to replicate Twitter’s animated launch image

For some time I’ve been wanting to demonstrate how to re-use your app’s launch image later on in the app. As a use case I picked Twitter’s seamless transition from a static to an animated launch image.

Over the holidays I finally found some time to give it a go. While creating the example I ran into several bugs I will also share the workarounds for.

NOTE: When (still?) using classic, wherever you read app/assets replace this with Resources and you’re good.

The end-result

Watch this movie to see both Twitter’s iOS launch image and the example I made in Titanium. The end-result on Android is identical.

The full example

Here’s all 80 lines needed for the example:

var win = Ti.UI.createWindow({
  backgroundColor: 'white'
});

var img = Ti.UI.createImageView({

  // 1. Finding the launch image
  image: (function getImage() {

    if (Ti.Platform.name === 'iPhone OS') {

      // Working around orientation-bug
      if (Ti.Platform.osname === 'ipad' || Math.max(Ti.Platform.displayCaps.platformWidth, Ti.Platform.displayCaps.platformHeight) === 736) {
        return 'Default-' + (Ti.Gesture.isPortrait() ? 'Portrait' : 'Landscape') + '.png';

      } else {
        return 'Default.png';
      }

    } else if (Ti.Platform.name === 'android') {
      return Ti.App.Android.R.drawable.background;
    }

  })(),

  // Working around 9-patch drawable bug
  width: Ti.UI.FILL,
  height: Ti.UI.FILL,

});

// 2. Fixing the background-shift on Android
if (Ti.Platform.name === 'android') {

  var view = Ti.UI.createView({

    // Working around pixel bug
    height: Ti.Platform.displayCaps.platformHeight / Ti.Platform.displayCaps.logicalDensityFactor,
    bottom: 0
  });

  view.add(img);

  win.add(view);

} else if (Ti.Platform.name === 'iPhone OS') {
  win.add(img);
}

// 3. Animating the image
win.addEventListener('postlayout', function onOpen(e) {
  win.removeEventListener('postlayout', onOpen);

  // win.backgroundColor = 'white';

  var imgWidth = img.rect.width,
    imgHeight = img.rect.height;

  img.animate({
    width: imgWidth * 0.8,
    height: imgHeight * 0.8,

    delay: 1000

  }, function after() {

    img.animate({
      width: imgWidth * 5,
      height: imgHeight * 5,

      opacity: 0,
      delay: 100
    });

  });

});

// 4. Opening the window
win.open({
  animated: false
});

1. Finding the launch image

iOS

For iOS, launch images are the Default*.png files that should be in your app/assets/iphone folder.

Although not in an images subfolder, you can use them like any other image. Titanium will automatically select the best file for the density (@2x, @3x), os-name (~ipad, ~iphone) and/or screen subtype (-736h, -667h, -568h).

Working around orientation-bug

However, at the moment it does not find orientation-specific images (-Landscape, -Portrait). So for iPhone 6 Plus and iPad we need to add this ourselves.

Android

On Android regular launch images (default.png) are normally found in density-specific folders under app/assets/android/images. The more preferable 9-patch images are found as background.9.png in the platform/android/res/drawable-* folders.

Titanium moves and renames the regular launch images to the drawable folders when it compiles. So in both cases we can use the Ti.App.Android.R.drawable.background to get the id of the application-resource in the required density and/or orientation.

Working around 9-patch drawable bug

If you are using a 9-patch image, use Ti.UI.SIZE for the width and height of the image. At the moment Titanium doesn’t load backgroundImages from the application-resources while an ImageView doesn’t support 9-patch images.

2. Fixing the background-shift on Android

Like it was on iOS 6 and earlier, on Android the launch image gets the full height of the screen, while the windows begin under the status bar. Because of this you’ll notice the launch image shifting down a little when we transition from the actual launch image to our full-size ImageView.

To fix this we wrap the ImageView in a view that we give the full height of the screen and position on the bottom of it. For a regular launch image we could do this directly on the ImageView, but this way it also works for a 9-patch launch image which would then be correctly centered in the wrapping view.

Working around pixel bug

Note that we have to convert Ti.Platform.displayCaps.platformHeight from pixels to dp. It’s one of the few remaining properties that don’t apply the default unit correctly.

3. Animating the image

We wait to animate the image until the first postlayout event after opening the window so we can read the exact dimension of the image and use that to calculate the animation.

We start with zooming out a little beforing zooming in and fading out the image. Note that we gave the window a white backgroundColor on line #2 so that zooming out and then fading out the image produces a smooth transitions.

In an actual app you would probably wait to animate the image until you’ve done whatever you need to do when your app first starts.

Finally we open the window with no animation. Depending on the distribution Android uses different animations to open windows which would disturb the seamless transition between the actual launch image and our re-use of it.

Finetuning

You could further finetune the animation by using TitaniumAnimator with one of its easing functions. For the sake of simplicity I have not done so in this example.

Update: Manuel Conde Vendrell did an Alloy version of this example, including some finetuning for Android 2.3 and an example of how to show the actual content after the animation ends.

Code strong!

App imagineer: Imagining, Engineering & Speaking about Native mobile Apps with Appcelerator Titanium & Alloy • Meetup organizer • Certified Expert • Titan


Comments