In Flash Professional CS5, when you use TLF text in your SWF, by default we do some tricks behind the scenes to preload TLF as a Runtime Shared Library (RSL) in a way that does not require you, the content author, to write any ActionScript to manage the preloading at all. However, if you are setting up your FLA with preloading in mind, where you use the first one or more frames as a preloading animation and then have ActionScript to move past this section once the loading is complete, then you should take advantage of the custom preloader loop feature which will work with your preloader animation and will only require a tiny bit of extra ActionScript. And in fact if you do not use the custom preloader loop feature, then your preloader animation will never be displayed.

General Preloading Example

Let’s walk through an example first and then I’ll go into some more explanantion of what is really going on. Flash CS5 has a sample template installed called Preloader for SWF. You can create a new FLA from this template by selecting the File -> New… menu item, then selecting the Templates tab, selecting the Sample Files category and finally select the Preloader for SWF template and click OK. This template is set up with some loading text on frame one along with some ActionScript on frame one to track the loading and display a percentage to the user until 100% is loaded, at which point it will go to frame two which has the real content.

To see how this preloader template is intended to work, select frame two of the Content layer in the Timeline panel and import some image. Next select the Control -> Test Movie menu item and you should see it go to your content on frame two seemingly instantly, which is because of course it did not have to download. If you keep the test movie window open and select View -> Simulate Download, you should now see the LOADING text and the download percentage display.

Adding TLF Breaks the Preloading Animation

Now try this: move the image off to the side, so it isn’t covering the “Large content here” text. Select that text and in the Properties panel change the combobox at the top from Classic Text to TLF Text. Now try the same steps again. First, when you test movie, you may get a warning dialog like this:

Your content will not stream. Runtime Shared Library (RSL) preloading will require all of your content to download before the first frame will play.  To prevent this you can change the Runtime Shared Library Settings in the Advanced ActionScript 3.0 Settings dialog which can be raised from the Publish Settings dialog.  The Runtime Shared Libraries being preloaded are:  textLayout_1.0.0.595.swz for TLF Text

So let’s talk briefly about what this dialog is saying. A preloader animation loop at the beginning of your timeline works in Flash because the player will start playing frames as soon as they are downloaded. This is what this dialog’s text means by “streaming”. So your preloader code and animation defined in frame one starts running as soon as all of frame one has downloaded, and while the user waits for the rest of your SWF to download, the preloader animation will run. However, the tricks we use to preload the TLF RSL before your content runs defeat this strategy. They require your entire SWF to download completely before frame one can be run. This is why it is so important to take advantage of the custom preloader loop which preserves your ability to stream your SWF and allows you complete control over the preloading experience.

Back to our example: if you keep the test movie window open this time and select simulate download, you will see a completely different preloading animation which is a line of five white circles that show and hide. So you might be wondering, what in the heck happened to my loading animation? As discussed in the previous paragraph, it is still there, but the preloading mechanism which we use to load the TLF RSL is taking precedence. It goes first, and it runs until your entire SWF content has loaded, which means that your preloading animation runs, but in a similar situation to how it runs in test movie mode without simulate download selected, which means it is dismissed instantaneously.

How to Use the Custom Preloader Loop Feature

You can take back control of your preloading by editing the preloader method in the Advanced ActionScript 3.0 Settings dialog. Raise this dialog by clicking on the Edit… button for ActionScript Settings in the Properties panel. If you do not see this button, click on the stage to select nothing so that the Document properties are displayed in the Properties panel. Once this dialog is raised, there are two important settings you need to change:

  • In the Library path tab, change the Preloader method from Preloader SWF to Custom preloader loop.
  • Near the top, change Export classes frame near the top from 1 to 2.

Your dialog should look like this when you are done:

Advanced ActionScript 3.0 Settings dialog

Now it is time to add some ActionScript to frame one of the Actions layer. I’ll walk through the details later, but for now the code in that frame will look like this, so you can just copy and replace the whole block:

import fl.events.RSLEvent;

stop();

var swfComplete:Boolean = false;
var rslComplete:Boolean = false;

this.loaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoading);
this.loaderInfo.addEventListener(Event.COMPLETE, onComplete);
this.addEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);

function onLoading(evt:ProgressEvent):void {
	var loaded:Number = evt.bytesLoaded / evt.bytesTotal; 
	percent_txt.text = (loaded*100).toFixed(0) + "%";
};

function onComplete(event:Event):void { 
	this.loaderInfo.removeEventListener(ProgressEvent.PROGRESS, onLoading);
	this.loaderInfo.removeEventListener(Event.COMPLETE, onComplete);
	swfComplete = true;
	if (rslComplete && swfComplete) {
		gotoAndStop(2); 
	}
};

function onRSLComplete(e:RSLEvent):void
{
	this.removeEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);
	rslComplete = true;
	if (rslComplete && swfComplete) {
		gotoAndStop(2); 
	}
}

Now try test movie and simulate download again, and you will find your loading animation working and restored. One thing you might notice when you simulate download is that you get a white screen for much longer than you did without TLF. This is because when you specify the export classes frame as frame two, what this really means is export all classes that are not required on frame one in frame two. There is quite a bit of ActionScript code that still needs to be on frame one, and you see the blank, white screen while you wait for this to download.

On a tangent, SWF size can be a concern with TLF. Even if the image I added to the FLA earlier, my published SWF is showing up as 59.4KB in the SWF History section of the Properties panel when I use TLF with a custom preloader loop. The good news is this is a bit smaller than the 61.2KB I get if I use the default Preloader SWF preloader settings, but the bad news is that it is much bigger than the 9.4KB when I use classic text. But on the other hand if I change the Default linkage setting in the Advanced ActionScript 3.0 Settings dialog to Merged into code, my SWF size balloons up to 187.4KB! So the RSL approach for TLF is still winning you quite a bit, and because it is deployed as a signed, cacheable RSL, or a SWZ file, by default, each user should only have to download the RSL once for every SWF that they ever run that uses it, which means with luck your SWF will not be the one that takes the download hit for the RSL.

How does it work?

Preloader Method: Custom Preloader Loop

So let’s talk about what you’ve done with the changes you’ve made. First I’ll explain the preloader method change to custom preloader loop. The preloader SWF method exports your SWF basically unchanged, and then wraps that SWF in another automatically generated SWF. We take advantage of the DefineBinaryData swf tag and the Loader.loadBytes API to make this work. However, when you select custom preloader loop, instead we inject the RSL preloader code directly into your SWF. While this has many benefits, the reason we cannot do this by default is that since you retain complete control over the timeline, you are required to take all the additional steps.

Export Classes Frame

The first additional step required is the change to the export classes frame. While in this example we changed it to frame two, note that in general it needs to be changed to the first frame after your preloading animation is complete. In other words, you cannot play the frame specified in the export classes frame until you have verified that all RSLs are done loading.

It is educational to see what happens if you leave the export classes frame set to one. So try going back into the Advanced ActionScript 3.0 Settings dialog and switch the 2 back to a 1 and test movie. The first thing you will notice is some errors in the output panel:

VerifyError: Error #1014: Class flashx.textLayout.container::ContainerController could not be found.

ReferenceError: Error #1065: Variable ComponentShim is not defined.

ReferenceError: Error #1065: Variable Font_5 is not defined.

ReferenceError: Error #1065: Variable MainTimeline is not defined.

If you go to the compiler errors panel, you will see a warning that actually explains your situation:

Warning: 5010: Use of a custom preloader loop with the RSL for TLF Text requires that the export classes frame be set after the end of the preloader loop to avoid VerifyErrors.

Essentially what has happened is we have forced some classes into frame two that depend on the classes defined within the RSL. If you attempt to load these classes by playing frame two before the RSL is finished loading, then bad things happen.

On a side note, while it is not really recommended to put any TLF on stage in your preloader animation, believe it or not bad things do not happen if you put some TLF text on frame one. Well not especially bad, anyways. Try this: restore your export classes frame back to 2, create a new layer on your timeline put some TLF text in frame one that new layer, so that it should show up on both frame one and two at runtime. If you test movie, you will not get any errors and your text will show up correctly. However, if you try a simulate download to see what it looks like, you should not see the TLF text display at all on frame one, during the preloading animation, but it will appear on frame two. We have smarts behind the scenes that fails as gracefully as possible and makes the right thing happen.

ActionScript

Finally, let’s dig into the ActionScript itself. Let’s look at what has been added to the original preloader code:

import fl.events.RSLEvent;

This is simply an import for the RSLEvent type. Many classes are imported for you automatically when writing ActionScript on frames in Flash Professional, but not the RSLEvent class.

var swfComplete:Boolean = false;
var rslComplete:Boolean = false;

Your script could be simpler when you were only waiting for one event to happen before proceding to frame two, but now that you need to wait for two events, you must use Boolean variables to track each of them separately.

this.addEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);

The RSL_LOAD_COMPLETE event is a new event that is dispatched by the main timeline when all RSLs have finished downloading. In this case the only RSL is for TLF, but you would still only need to listen for this one event no matter how many RSLs were downloading.

	swfComplete = true;
	if (rslComplete && swfComplete) {
		gotoAndStop(2); 
	}

This code was added to the onComplete function. It marks swfComplete = true to indicate that the swf loading is finished, and then checks to ensure that rsl loading is complete before going to frame two.

function onRSLComplete(e:RSLEvent):void
{
	this.removeEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);
	rslComplete = true;
	if (rslComplete && swfComplete) {
		gotoAndStop(2); 
	}
}

This is the event listener function for the RSL_LOAD_COMPLETE event. The code is very similar to the code in the onComplete method, except for it sets the rslComplete Boolean to true.

Really pretty simple code. Not just one or two lines, but not too much more than is already required to make a preloader loop like this work.

Now that Flash CS5 is out, it seems that many users have been trying to use workflows to which they are accustomed with TLF text and have been having problems. We did know that there would be some problems like this with TLF, but I think we really underestimated the pain it would cause people, so apologies for that.

The main problem (there are others, but maybe another post sometime for all that) occurs when you a SWF that uses TLF and you intend to load that SWF using Loader or UILoader into another application and you want to do scripting across the parent and child, even if it is as simple as calling something like myLoader.content.gotoAndStop("myFrame").

Because of the way we do the runtime shared library (RSL) preloading for TLF with the default options set, this doesn’t work as you would expect. There is actually a tech note about this on adobe.com at http://kb2.adobe.com/cps/838/cpsid_83812.html.. However, it needs some corrections as currently written, so I’d like to explain those and also to clarify things a bit here.

SafeLoader work around

First, I would like to evangelize a bit for the SafeLoader solution, listed as solution 2. Really I would list that as solution 1. It is a class that emulates the Loader’s API, but is not in fact a Loader or a subclass of Loader, and if you use the SafeLoader instead of Loader, then you can simply load a child SWF that uses TLF as normal and everything will just work. Even better, you can use the SafeLoader to load a child SWF that does not use TLF and everything will just work.

Typically all you need to do to use the SafeLoader is:

  • Download the source from the tech note and install the files SafeLoader.as and SafeLoaderInfo.as in the folder structure fl/display next to your FLA file (or somewhere else in your source path).
  • In your source import fl.display.SafeLoader
  • Search and replace through your source, replacing all instances of Loader with SafeLoader

The SafeLoader.zip file includes some FLA files which are examples of how to use the SafeLoader class and you don’t really need those, but you can check them out. Honestly it is a little embarrassing that they included those files with the download. Now you can see the pathetic level of design skill the typical engineer on the Flash pro team has. We draw a lot of ellipses.

If you are wondering, why didn’t we ship the SafeLoader with Flash? It was something that we put together very late in the game and there was not time.

Custom preloader loop work around

Now on to what is listed in the tech note as solution 1. This is really two solutions which have been munged together.

So the one that I would recommend first is to leave the runtime shared library (RSL) default linkage set to RSL and change the preloader method to custom preloader loop. (This custom preloader loop thing is another topic that really needs its own blog post.) This setting is changed in the Library path tab of the Advanced ActionScript 3.0 Settings Dialog, which you can raise from the document level property inspector. The dialog is shown below with its default settings.

Advanced ActionScript 3.0 Settings Dialog

Advanced ActionScript 3.0 Settings Dialog

After changing the preloader method to custom preloader loop, you then must follow the steps outlined in the tech note (and not after setting the default linkage to merged into code as the note states):

  • Leave frame one in the main timeline of the child SWF empty. Your content, including frame scripts, should start on frame 2.
  • In the Advance ActionScript 3.0 Settings Dialog, set Export Classes in frame 2.

Part of the problem with this method is that while your child SWF will load and run correctly when you put it in the parent, you actually may get runtime errors when you test movie the child SWF and try to work with it individually. If this happens, the way to work around it is to temporarily go into the Advanced ActionScript 3.0 Settings Dialog and switch the default llinkage to be merged into code. Then you would need to remember to set it back when you do your final publish. I think you can see why I recommend SafeLoader over this!

Merged into code work around

This is a simple work around, but it has a major draw back. You simply go into the Advanced ActionScript 3.0 Settings Dialog and set default linkage to merged into code for any child SWF that uses TLF.

The major drawback here is that now every child SWF that uses TLF is going to link the whole TLF library in, which is going to increase the size of that SWF by about 120k for every SWF. However, if you download TLF as an RSL, we have set it up to be downloaded as a cross domain RSL, also known as a SWZ file or a signed, cached RSL. This means that a user will only ever need to download the TLF library once for every single site that uses it. So chances are pretty good that if you set up TLF to be downloaded as an RSL, that your flash application will not even need to be the one that causes that extra 120k to download, but that it will already be there, ready and waiting.

So hope that helps clarify the work arounds for this very annoying problem.

Haven’t posted in a while, and i really thought this announcement was done well. I got a huge kick out of the video they showed at the MAX keynote on Monday.

So it is true! In your hands in a public beta to be out this year, as Johnny L said, you can use Flash Professional CS5 and make iPhone apps. I haven’t actually played with it at all, as I don’t own an iPhone (many of my co-workers mock me as a luddite for this) but it looks awfully slick.

More info on the public beta is up at adobe labs.

And while you are at labs, check out the fresh off the presses Flash Builder and Flash Catalyst betas! Good stuff.

Just recently got a question from someone on how to work around the fact that you cannot create custom component UI for Flash Authoring components with AS3. We are all enjoying this new world of AS3, so what a pain to have to create custom comonent UI, even for your AS3 components, using AS2!

Now it is very reasonable to assume that it isn’t possible to create custom component UI with AS3, because there is no documentation and no article, no nothing, saying how to do this. And why would we go to all the effort to code and test a feature and not TELL anyone about it. We wouldn’t do that, right?

Well, we did that.

Note that this stuff doesn’t work in Flash CS3, but it will work in CS4. So if someone tries to use a component using an AS3 custom UI, the UI will show up in CS3 but it won’t actually work. So that is a pain.

OK, let’s back up. What is a custom component UI. This ancient article explains the very basic idea of it. Basically it is a SWF that shows up in the component inspector instead of the list of name/value pairs. You connect it to your component via the Component Definition dialog, which I’m sure you are already familiar with if you create components.

I made a sample AS3 custom component UI and you can download the zip. In the zip, you’ll find

  • CustomUIAS3.fla and .as, the source for the custom UI.
  • CustomUIAS3TestComponent.fla and .as, the source for the component that uses the custom UI.
  • ComponentTest.fla, a test file with the final published component on stage and the custom UI working.

You can immediately open ComponentTest.fla and play around with the interface. It looks like this:

Custom User Interface in AS3

Mine is a little ugly in that it requires you to click the Update button at the bottom for the version on stage to be updated. However, there is no reason that a real UI needs to do this. It can use all the events available in AS3 to be clever about when to update the instance on stage.

The basics are pretty basic, and I think definitely cleaner and nicer than the old AS3 xch way. Instead, it uses ExternalInterface.

To get your custom UI updating based on selection in authoring, you need to implement a public function onUpdate(...updateArray:Array):Boolean and expose it via ExternalInterface. You can either do this by making connecting a custom class to you main timline (which is what i did) or you can put the functions in a frame script on your main timeline. This function should always return true.

The updateArray that is passed in is an alternating list of name/value pairs. So for example if your component has two properties, label:String and num:Number, you might get the array: [ "label", "asdf", "num" 0 ]. You just need to step through this array and update your UI as appropriate.

Passing the updated information back uses an array formatted the same way. In fact in my example, I found it useful to stash the array passed into onUpdate(), update that array and then pass it back. The way you call back into Flash with your update is by calling ExternalInterface.call("updateProperties", propertiesArray).

You can even support complex types this way. In my example, I set a dataProvider property on a ComboBox component. With a collection type like this, instead, of getting a simple value in the array, you get an Object with a collectionArray:Array on it. I think my example spells out how to work with it pretty well.

I’ll leave you with the code for the CustomUIAS3 class. Hope you found this helpful!


package {
	import fl.controls.*;
	import fl.events.*;

	import flash.display.*;
	import flash.events.*;
	import flash.external.*;

	public class CustomUIAS3 extends Sprite {

		public var label_txt:TextInput;
		public var toggle:CheckBox;
		public var color:ColorPicker;
		public var dataGrid:DataGrid;
		public var num_txt:TextInput;
		public var plus_btn:Button;
		public var minus_btn:Button;
		public var submit_btn:Button;

		private var propertiesArray:Array;

		public function CustomUIAS3()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;

			if (ExternalInterface.available) {
				ExternalInterface.addCallback("onUpdate", onUpdate);

				submit_btn.addEventListener(MouseEvent.CLICK, updateAuthoring);
			}

			plus_btn.addEventListener(MouseEvent.CLICK, doPlus);
			minus_btn.addEventListener(MouseEvent.CLICK, doMinus);
		}

		private function doPlus(e:Event):void {
			dataGrid.dataProvider.addItem({label: "label", data: "data"});
		}

		private function doMinus(e:Event):void {
			dataGrid.dataProvider.removeItemAt(dataGrid.selectedIndex);
		}

		public function onUpdate(...updateArray:Array):Boolean
		{
			propertiesArray = updateArray;

			for (var i:int = 0; i + 1 < propertiesArray.length; i += 2) {
				var name:String = String(propertiesArray[i]);
				var value:* = propertiesArray[i+1];
				switch (name) {
				case "label":
					label_txt.text = value;
					break;
				case "toggle":
					toggle.selected = value;
					break;
				case "color":
					color.selectedColor = value;
					break;
				case "num":
					num_txt.text = String(value);
					break;
				case "dataProvider":
					var collectionArray:Array = value.collectionArray;
					var dp:DataProvider = dataGrid.dataProvider;
					dp.removeAll();
					for (var j:int = 0; j < collectionArray.length; j++) {
						var item:Object = collectionArray[j];
						dp.addItem({label: item.label, data: item.data });
					}
					break;
				}
			}

			return true;
		}

		private function updateAuthoring(e:Event):void
		{
			for (var i:int = 0; i + 1 < propertiesArray.length; i += 2) {
				var name:String = String(propertiesArray[i]);
				switch (name) {
				case "label":
					propertiesArray[i+1] = label_txt.text;
					break;
				case "toggle":
					propertiesArray[i+1] = toggle.selected;
					break;
				case "color":
					propertiesArray[i+1] = color.selectedColor;
					break;
				case "num":
					propertiesArray[i+1] = Number(num_txt.text);
					break;
				case "dataProvider":
					var propObj:Object = propertiesArray[i+1];
					var collectionArray:Array = new Array();
					var dp:DataProvider = dataGrid.dataProvider;
					for (var j:int = 0; j < dp.length; j++) {
						var item:Object = dp.getItemAt(j);
						collectionArray.push({label: item.label, data: item.data});
					}
					propObj.collectionArray = collectionArray;
					break;
				}
			}

			ExternalInterface.call("updateProperties", propertiesArray);
		}

	}

}

I wrote this article ages ago, and it was originally had “for CS3” in the title I think, but they took it out when I told them it didn’t really need to be updated for CS3. It has oodles of information in it about the User Interface AS3 components included with Flash Authoring.

http://www.adobe.com/devnet/flash/articles/creating_as3_components.html

So this is hardly like hot off the press news, but this article is useful and I fully expect I will reference it in my posts so that I can avoid typing all that stuff again.

I’ve been meaning to do this for ever, and I’m finally actually starting my blog. My idea is this blog is going to focus on some Flash things that I know a thing or two about, like ActionScript 3.0 and Flash Authoring Components. My idea is that I will be putting out useful information like tips, known bugs with work arounds, code snippets accompish cool stuff, etc.

But I should introduce myself, first. I’m Jeff Kamerer, and I’ve been on the Flash Authoring team since October 2002. I’ve worked on many features, including the FLVPlayback component (both AS2 and AS3 versions), support work for the AS3 UI components, ActionScript 3.0 support and Flex integration.

Over time I have continually found myself answering questions on mail threads, either internally at Adobe or on beta lists or whatever, and thought, “Wow, I bet a lot of people out there in the world would find this stuff useful! I should write an article.” Well, I have written some articles, but generally I find that pretty time consuming, so I realized if I am ever to get this information out into the world, a blog would be the best way.

So here it is, my blog! How exciting.