iOS App Launch blink fix

If you have been developing apps for iOS with AIR, I am almost sure that you have come across the issue with the “App launch blink” that appears between, when your app has launched( showing the Launch Image) and when your app is instantiating its views.
Here I will explain a simple way to fix this annoying issue.

EDIT: Jeff Ward pointed out in the comments section, that you can actually just let the Bitmat stay, until your Starling has loaded and initialized, and then just remove the Bitmap.
This is possible because the “Normal” flash display list is above the Stage3D. I don’t know why I didn’t think of that :)

You need to apply these 2 steps in your app.

  1. Add a flash.display.Bitmap copy of your LaunchScreen image to your Document Class.
  2. when everything (loading texures, initialising view, ect) is finished. Fade that flash.display.Bitmap out.

Step 1

This is in you Document Class where you set up your Starling instance.

// flash.display.Bitmap
private static var _background:Bitmap;

private function initializeStarling(stage:Stage):void
{
     stage.align = StageAlign.TOP_LEFT;
     stage.scaleMode = StageScaleMode.NO_SCALE;
     stage.frameRate = 60;
     var viewPort:Rectangle = new Rectangle(0, 0, stage.fullScreenWidth, stage.fullScreenHeight);

     // Find out what device is running and create a flash.display.Bimap
     if (Globals.scale == 1)
     {
          _background = new Embeds.loading_1x_iphone();
     }
     else
     {
          _background = Globals.isIphone5 ? new Embeds.loading_2x_iphone5() : new Embeds.loading_2x_iphone();
     }
     // Add the flash.display.Bitmap on top of everything. // Remember Flash DisplayList is above the Stage3D
     addChild(_background);

     // Setup Starling
     _starling = new Starling(MainStarlingView, stage, viewPort);
     .... Other Starling setup code...
     _starling.addEventListener(starling.events.Event.ROOT_CREATED, function rootCreated(event:Object, main:MainStarlingView):void
     {
          _starling.removeEventListener(starling.events.Event.ROOT_CREATED, rootCreated);
          main.initialize();
          _starling.start();
     });
}

// This will be called from your Main Starling View when
// all is loaded and initialized.
public static function removeLaunchImage( onRemoved:Function ):void
{
     Starling.juggler.tween(_background, 1,
     {
          alpha:0,
          onComplete:function():void
          {
               _background.parent.removeChild(_background);
               _background.bitmapData.dispose();
               _background = null;
               onRemoved();
          }
     });
}

Step 2

This is in your Main Starling Class.


public function initialize():void
{
     var appDir:File = File.applicationDirectory;
     var device:String = "iphone";
     var scale:int = Globals.scale;
     var texturepacker:String = formatString("assetifier/assets/texturepacker/{0}/{1}x", device, scale);
     var glyphdesigner:String = formatString("assetifier/assets/glyphdesigner/{0}/{1}x", device, scale);

     Globals.AM = new AssetManager(scale);
     Globals.AM.verbose = true;
     Globals.AM.enqueue
     (
          appDir.resolvePath(texturepacker),
          appDir.resolvePath(glyphdesigner)
     );
     Globals.AM.loadQueue(function progress(value:Number):void
     {
          // All Loaded
          if (value == 1)
          {
               // trace("ASSETS LOADED");
               removeLaunchAndInit();
          }
     });
 }

private function removeLaunchAndInit():void
{
      _gameView = new GameView(new GameModel());
      addChild(_gameView);
      ThreeCrates.removeLaunchImage( _gameView.start );
}

  • Hmm – I don’t think it’s necessary to pass the Bitmap to starling and create an Image from the Bitmap. This causes an extra full-screen texture upload (via the Text.fromBitmapData call).

    If you make that background public static on your main class (let’s say it’s Main.as), then later in your game class after it’s all initialized, you can just call:

    Main.background.parent.removeChild(Main.background);

    Or fade it out, if you like.

    This is because the Flash stage is above the Starling stage – so just leave the Bitmap there as long as you like while your Starling classes initialize.

    • bjeld

      Good idea Jeff! Never thought of that. Will try that out.

      Edit: Just tried it out – Worked flawless and I could remove about 15 lines of code!
      Thank you for the tip, guess this i why its good to blog :)

      I will update my blogpost.

      ps.
      in my DocumentClass I Added:

      public static function removeLaunchImage( onRemoved:Function ):void {
              Starling.juggler.tween(_background, 1,
              {
                  alpha:0,
                  onComplete:function():void
                  {
                      _background.parent.removeChild(_background);
                      _background.bitmapData.dispose();
                      _background = null;
                      onRemoved();
                  }
              });
          }
      

      And from my Main i now only call:

      private function removeLaunchAndInit():void {
              _gameView = new GameView(new GameModel());
              addChild(_gameView);
              ThreeCrates.removeLaunchImage( _gameView.start );
          }
      
      • Excellent, glad it worked!

  • Donny

    I’ve always just embedded the launch image/images, so this issue has never popped up:

    [Embed(source = "/../export/Default-Landscape@2x.png")] private static const Loading:Class;
    private static const __loading : Bitmap = new Loading();

  • iOS App Launch blink fix – Bjeld