So there’s a problem with using gotoAndStop() in AS3 classes, as soon as you call it, you temporarily lose access to items on stage (on the timeline) whether they are defined as member variables, or using getChildByName(). This is different from AS2, items on stage were immediately accessible.

Why use the timeline at all? For one you might have a simple button using named keyframes as button states, or when dealing with assets created by designers that include animations with portions that require localisation of text. So like before you use gotoAndStop() or gotoAndPlay() to manage which “state” your MovieClip is in, but when you go to access anything on stage, it is null, even if it was on the previous keyframe. Here’s a snippet from a typical AS3 class:


...
public var myTitleField: TextField;

protected function onAddedToStage( event: Event ): void
{
  myTitleField.text = "Step" + currentStep; // All good
}
...

But how about changing the current frame as a result of a mouse click for example:


protected function showNextStep(): void
{
  gotoAndStop( "step" + currentStep );
  myTitleField.text = "Step" + currentStep; // myTitleField is null!
}

Ouch, so unlike AS2, you cannot reference something on stage after a gotoAndStop()… I know! Wait a frame!? Afraid not. Waiting a frame (using a callLater or simply hooking into one ENTER_FRAME event broadcast) will not be long enough. But there is another event dispatched by Stage which might work, Event.RENDER.

I think the event sequence goes something like this:

  1. myTitleField is defined here
  2. gotoAndStop( “step2” );
  3. myTextField is null here
  4. Event.ENTER_FRAME is dispatched
  5. myTitleField is still null here
  6. Any code written on the keyframe itself is executed
  7. stage’s Event.RENDER is dispatched
  8. myTitleField is defined again!

So thanks to a tip from Senocular, the RENDER event looks like what we need. To force this event to fire, you must call stage.invalidate(), also the event is only dispatched to items on a DisplayList, and on top of that, it doesn’t go through a typical capture phase, the event is broadcast directly to the DisplayObject, but that shouldn’t matter here.

Ok so a sample might now look like this:


...
public var myTitleField: TextField;

protected function onAddedToStage( event: Event ): void
{
  myTitleField.text = "Step" + currentStep; // All good
  stage.addEventListener( Event.RENDER, onStageRender );
}

protected function showNextStep(): void
{
  gotoAndStop( "step" + currentStep );
  stage.invalidate();
}

protected function onStageRender( event: Event ): void
{
   myTitleField.text = "Step" + currentStep; // myTitleField is back!
}

So that’s fairly crude, but the idea is there. I was speaking to Tink who suggested that we override the gotoAndStop/Play methods in a base class to automatically call stage.invalidate().

Really you want to wrap all of this up in a base class and hook it into a redraw cycle so that you don’t have to add the stage RENDER listener and handler each time you need to do this.

In my case, I have a base View class that contains some simple functionality such as a Flex-like initialization phase and callLater method. I’ve also added these overriden methods and in my case they call invalidate() on my base class, which invokes the “component-like” redraw function whenever a property is changed and it’s time to update the visuals.

My initial reason for doing this was because of shortcomings in Flash’s built in SimpleButton class, which doesn’t appear to allow for localisation or font embedding when switching states, so I ported an AS2 SimpleButton I had written.

This issue is a major annoyance as without using this workaround you are basically locking out designers from working on FLAs, and using code for everything, which isn’t always the best approach in highly creative work, it isn’t even always possible.

Anyway, I hope this proves useful, there’s a couple of other solutions out there but this one feels the most processor friendly and doesn’t rely on essentially “polling” the ADDED event, or ENTER_FRAME until your on stage element appears in memory.

Note: It’s important to remember that there are bugs related to both Event.ADDED_TO_STAGE and Event.RENDER (with wmode). So best be sure your viewers are using Flash Player 9.0.115.0 or greater to avoid a world of pain 🙂