Objective-C for AS3.0 Developers: How to automate getters & setters

In AS3.0 we sometimes have to type a bunch of repetitive lines of code to create Getter & Setter functions that look like this:

protected var _property1:String;
protected var _property2:String; 
//...
public function set property1(p1:String):void { _property1 = p1; }
public function get property1():String { return _property1; }
public function set property2(p2:String):void { _property2 = p2; }
public function get property2():String { return _property2; }
//...	

There’s a shortcut in Objective-C.

If you want, you can manually write out getter & setter methods. It might be useful for those who prefer clarity more than using code shortcuts. However, in this example, the lines that start with @property in the header file and @synthesize in the implementation file allow you to skip the often tedious task of typing up Getters & Setters:

//MyClass.h file
@interface MyClass: NSObject
{
    int *property1;
    int *property2;
}

@property(nonatomic, readwrite) int *property1;
@property(nonatomic, readwrite) int *property2;

//...
@end

//MyClass.m file
@implementation MyClass

@synthesize property1;
@synthesize property2;

//...
-(void)dealloc
{
    [text release];
    [super dealloc];
}
@end

An example from UIKit framework

@property calls are listed in the iPhone equivalent of the asdoc (AS3.0 api documentation). In this case, for a class called UIImageView, and it’s property animationRepeatCount, the documentation lists:

@property(nonatomic) NSInteger animationRepeatCount 

Which means you can use setAnimationRepeatCount as the Setter method for animationRepeatCount whenever you have a variable of type UIImageView. Here’s a video tutorial that uses the setter for this property.

You can also see @property calls in the UIImageView.h header file in the SDK folder on your machine. The path is something like:
YOUR_HARD_DRIVE/Developer/Platforms/iPhoneOS.platform/
Developer/SDKs/iPhoneOS3.1.3.sdk/System/Library/Frameworks/
UIKit.framework/Headers/UIImageView.h

Objective-C for AS3.0 Developers: How to create a new object

In AS3.0 you create a new Object, or instance of a Class, and store it in a reference (or variable) like so:

private var myRef:MyClassName = new MyClassName();
private var myRef:MyClassName = new MyClassNameSubclass();

In Objective-C 2.0, there’re several ways to do this.

Here’s 1 way:

MyClassName *myRef = [[MyClassName alloc] init];

Here’s another:

MyClassName *myRef; //create the reference/variable
myRef = [MyClassName alloc]; //allocate memory of size MyClassName
myRef = [myRef init]; //initiate the MyClassName instance called myRef

And another:

MyClassName *myRef = [MyClassName new];

Why do we have to bother allocating memory manually? AS3.0 developers should be familiar with the concept of Garbage Collection. Objective-C does support GC but not everywhere. For example, the iPhone doesn’t allow it, apparently because of concerns over battery life.

In case you’re wondering, alloc and init are methods inherited from NSObject. Most Objective-C classes in the Foundations Framework inherit from NSObject.

Objective-C 2.0 also allows for dot (.) syntax that Flash/Flex/Actionscript developers are used to.

However, it seems like object instantiation doesn’t fully work with dot syntax. I’m no expert but when I try to write “MyClassName.alloc()” or “MyClassName.alloc” I get errors.
This worked, however:

NSAutoreleasePool * pool = [NSAutoreleasePool alloc]; 
pool = pool.init; 

/*
MyClassName *myRef =  [MyClassName alloc];
myRef = myRef.init;
*/

I’m guessing this means that dot syntax works with Instance methods but not with Class methods.

Embedding fonts on Mac with AS3

Recently noticed that some standard code that works fine for embedding Open Type and True Type fonts in an AS3 application in the Flash IDE, doesn’t always work for some Postscript fonts.

It turns out that only Type 1 Postscript fonts can be embedded in Flash. If the Postscript font is Type 0, Type 5, or Type 9 it won’t work. That’s what this Adobe quickstart page seems to imply.

Here’s an example that worked fine for True Type, Open Type but not for some Postscript fonts:

//...
			font1 = new ArialFont1(); //12pt 
		//...			
			myFormat2 = new TextFormat();
			myFormat2.font = font1.fontName;
			myFormat2.size = 13; 
			myFormat2.leading = 2;
			myFormat2.color = 0x000000; 
		//...	
			subHeader = new TextField();	
			subHeader.defaultTextFormat = myFormat2; 
			subHeader.width = 300;
			subHeader.height = 150; 			
			subHeader.embedFonts = true;
			subHeader.wordWrap = true;
			subHeader.multiline = true; 	
			subHeader.x = 175; //110
			subHeader.y = 96;
			subHeader.htmlText = "place holder text...";
			addChild(subHeader);		
//...

AS3 scrollbar variation

Here’s a quick rewrite of a fun little basic scrollbar class written by Flashscaper.

The original was written pretty good and it was easy to drop into a project but there was one thing that would’ve made it more difficult for others to find out what’s happening in my code. The original Scrollbar.as was assigned directly to Scrollbar MovieClip via the Library’s Properties panel, rather than being instantiated through code. This technique works OK, but if some other developer had to pick up my code a year from now it would’ve been more difficult for her to find what’s where. To solve this issue, I rewrote the Scrollbar class so it can be started up via composition in Actionscript.

Made a few other small changes, like switching the animation code from Tweener to TweenLite. Had to move the stage listeners like this one:
“stage.addEventListener(MouseEvent.MOUSE_UP, stopScroll);”
to the “user” or client class called Main and use ScrollbarInstanceName.stopScroll to access public methods like stopScroll().

package com.flashscaper
{	
	import com.greensock.TweenLite; //TweenLite v.11.1
	import com.greensock.easing.Sine;	
	
	import flash.events.MouseEvent;	
	import flash.events.Event;	
	import flash.display.MovieClip;
	import flash.display.Sprite;	
	import flash.geom.Rectangle;

	public class BasicScrollbar extends MovieClip 
	{
		//data is "protected" instead of "private" so 
		//this class can be subclassed to add additional functionality 
		protected var myBar				:MovieClip; 		
		protected var target			:MovieClip;
		protected var top				:Number;
		protected var bottom			:Number;
		protected var dragBot			:Number;
		protected var range				:Number;
		protected var ratio				:Number;
		protected var sPos				:Number;
		protected var sRect				:Rectangle;
		protected var ctrl				:Number;
		protected var trans				:String;
		protected var timing			:Number;
		protected var isUp				:Boolean;
		protected var isDown			:Boolean;
		protected var isArrow			:Boolean;
		protected var arrowMove			:Number;
		protected var upArrowHt			:Number;
		protected var downArrowHt		:Number;
		protected var sBuffer			:Number;
		
		public function BasicScrollbar(t1:MovieClip, tr1:String, tt1:Number, sa1:Boolean, b1:Number) 
		{ 					 
			init(t1, tr1, tt1, sa1, b1); 
		}		
		
		public function	createBar():void 
		{			
			myBar = new Scrollbar_Lib_Asset(); //Library asset in the main .fla 					
			myBar.x = 201;
			myBar.y = 17;			
			addChild(myBar);
			
			myBar.scroller.addEventListener(MouseEvent.MOUSE_DOWN, dragScroll);					

		}		
		
		public function init(t:MovieClip, tr:String,tt:Number,sa:Boolean,b:Number):void {
			
			createBar();								
			
			target = t;
			trans = tr;
			timing = tt;
			isArrow = sa;
			sBuffer = b; 						

			if (target.height <= myBar.track.height) {
				this.visible = false;
			}			
						
			myBar.upArrow.buttonMode = true; //library & stage asset
			myBar.downArrow.buttonMode = true; //library & stage asset 
			
			upArrowHt = myBar.upArrow.height;
			downArrowHt = myBar.downArrow.height;
			if (isArrow) {
				top = myBar.scroller.y;
				dragBot = (myBar.scroller.y + myBar.track.height) - myBar.scroller.height;
				bottom = myBar.track.height - (myBar.scroller.height/sBuffer);

			} else {
				top = myBar.scroller.y;
				dragBot = (myBar.scroller.y + myBar.track.height) - myBar.scroller.height;
				bottom = myBar.track.height - (myBar.scroller.height/sBuffer);

				upArrowHt = 0;
				downArrowHt = 0;
				removeChild(myBar.upArrow);
				removeChild(myBar.downArrow);
			}
			range = bottom - top;
			sRect = new Rectangle(0,top,0,dragBot);
			ctrl = target.y;
			//set Mask
			isUp = false;
			isDown = false;
			arrowMove = 10;
			
			if (isArrow) {
				myBar.upArrow.addEventListener(Event.ENTER_FRAME, upArrowHandler);
				myBar.upArrow.addEventListener(MouseEvent.MOUSE_DOWN, upScroll);
				myBar.upArrow.addEventListener(MouseEvent.MOUSE_UP, stopScroll);
				//
				myBar.downArrow.addEventListener(Event.ENTER_FRAME, downArrowHandler);
				myBar.downArrow.addEventListener(MouseEvent.MOUSE_DOWN, downScroll);
				myBar.downArrow.addEventListener(MouseEvent.MOUSE_UP, stopScroll);
			}
			var square:Sprite = new Sprite();
			square.graphics.beginFill(0xFF0000);
			square.graphics.drawRect(target.x, target.y, target.width+5, (myBar.track.height+upArrowHt+downArrowHt));
			
			//trace("parent = " + parent); // = null			
			addChild(square); 
			
			target.mask = square;			
		}
		
		public function upScroll(event:MouseEvent):void 
		{
			isUp = true;
		}
		
		public function downScroll(event:MouseEvent):void 
		{
			isDown = true;
		}
		
		public function upArrowHandler(event:Event):void 
		{
			if (isUp) {
				if (myBar.scroller.y > top) {
					myBar.scroller.y -= arrowMove;
					if (myBar.scroller.y < top) {
						myBar.scroller.y = top;
					}
					startScroll();
				}
			}
		}
		
		public function downArrowHandler(event:Event):void 
		{
			if (isDown) {
				if (myBar.scroller.y < dragBot) {
					myBar.scroller.y += arrowMove;
					if (myBar.scroller.y > dragBot) {
						myBar.scroller.y = dragBot;
					}
					startScroll();
				}
			}
		}
		
		public function dragScroll(event:MouseEvent):void 
		{			
			myBar.scroller.startDrag(false, sRect);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, moveScroll);
		}
		
		public function mouseWheelHandler(event:MouseEvent):void 
		{
			if (event.delta < 0) {
				if (myBar.scroller.y < dragBot) {
					myBar.scroller.y-=(event.delta*2);
					if (myBar.scroller.y > dragBot) {
						myBar.scroller.y = dragBot;
					}
					startScroll();
				}
			} else {
				if (myBar.scroller.y > top) {
					myBar.scroller.y -= (event.delta*2);
					if (myBar.scroller.y < top) {
						myBar.scroller.y = top;
					}
					startScroll();
				}
			}
		}
		
		public function stopScroll(event:MouseEvent):void 
		{
			isUp = false;
			isDown = false;
			myBar.scroller.stopDrag();

			stage.removeEventListener(MouseEvent.MOUSE_MOVE, moveScroll);
		}
		
		public function moveScroll(event:MouseEvent):void 
		{
			startScroll();
		}
		
		public function startScroll():void 
		{
			ratio = (target.height - range)/range;
			sPos = (myBar.scroller.y * ratio)-ctrl;
			
			TweenLite.to(target, timing, {y:-sPos, ease:trans}); 
		}
	}
}

An example “user” class for the Flash IDE that creates an instance of the scrollbar:

package {
		
	import com.flashscaper.BasicScrollbar;
	
	import flash.display.MovieClip;
	import flash.events.MouseEvent;

	public class Main extends MovieClip {
		
		private var scrollBr:MovieClip;
		private var whatToSroll:MovieClip;
				
		public function Main() 
		{ 		
			whatToSroll = new TextMC();	//Library asset (MovieClip with anything you like inside)			
			addChild(whatToSroll);
			
			scrollBr = new BasicScrollbar(whatToSroll, "Sine.easeOut", 2, true, 2); //uses a Library asset: Scrollbar_Lib_Asset			
			stage.addEventListener(MouseEvent.MOUSE_UP, scrollBr.stopScroll);
			stage.addEventListener(MouseEvent.MOUSE_WHEEL, scrollBr.mouseWheelHandler);			
			addChild(scrollBr);			
		}				
	}
}

Fun with Google Analytics in the Flash IDE with AS3

ExternalInterface.call()
There’s always External Interface method. Some good tutorials on this out there too.

So far I found 1 Flashcoders post about a bug in IE7 that doesn’t allow the use of this technique.

nagivateToURL()
People seem to be complaining about unwanted popup windows with this method. It seems to work fine for me in the browser, both locally and off a server. Popup windows only show up with Test Movie, which, while annoying, will not be an issue with clients.

Google’s AS3 component / SWC library
Google has an Analytics component and an Analytics Library component that doesn’t require any javascript on your page and talks to their analytics system directly. Only problem seems to be it adds between 60KB and 40KB to your file size. The cool part – you don’t have to rely on Javascript at all.

SWFaddress for Google Analytics tracking
There’s a way to use SWFaddress 2.0 together with ga.js to track page views in Flash sites.

sendToURL()
There’re a few posts from 2007 that utilize the global function sendToURL().

/* ... */
var trgtURL:URLRequest = new URLRequest("javascript:pageTracker._trackPageview('mypath/mypage_link');");
sendToURL(trgtURL);
/* ... */

This technique wouldn’t work & gave me a security error every time, both during Test Movie and from a live server. I suspect the Flash player security changes between 9,0115 and 10 banned this method. Here’s the error: “Security Error: Error #2169: The method sendToURL may not be used for browser scripting. The URL javascript:pageTracker._trackPageview…”

getURL() ?
Oh, I hope not…

Something like this should work OK, given you import all the proper classes & Google’s javascript code is on the html page that displays your .SWF:

//...       
       trackTheGoogley("mysite.com/mycategory/mytrackerTextHere");

		public function trackTheGoogley(trackString:String):void
		{			
			if(ExternalInterface.available){
				ExternalInterface.call("pageTracker._trackPageview",trackString);
			} else {
				navigateToURL(new URLRequest("javascript:pageTracker._trackPageview('"+trackString+"')"), "_self");
			}
		}
//...     

Part 2: Using custom graphics via Flex SDK in the Flash IDE

This post expands on the previous simple example with an example of a basic menu for an Actionscript-only AS3.0 project in Flash that uses a cool feature of the Flex SDK — the Embed tag. The last example was useful because the Flash IDE’s Library wasn’t used at all. This method, allows you to still use the Flex SDK’s [Embed] meta tag but lets you work with a Designer who only knows how to use .fla files and the Library. Using this technique a Developer can receive an assets SWF from a Designer and worry about the code, not about setting up graphics in the library.

Prep the files
In the Flash IDE* (CS4, for example), create an empty .fla and set TenOfSameButn as the Document Class. Make sure you have an assets SWF named “BasicBtnAssets.swf” inside a folder called /assets in your main directory, or in the same place your main .fla is located. BasicBtnAssets.swf should contain your button graphics, in this case just a generic Off state and On state set to Export for Actionscript. The Off state has a Class name of “reg” (set by going to Library > (Right/Control click the asset MovieClip ) > Properties) and the On state is named “ovr” via the library by the Designer.

Create the graphic assets container Class
Place it inside this folder structure: {your main .fla’s location}/com/timshaya/model.

package com.timshaya.model
{	
	import flash.display.Sprite;
	import flash.display.MovieClip;
	
	public class BasicBtnAssets extends Sprite
	{	
		//embed graphic assets, 
		// "../../../" since "/assets" is outside this package
		[Embed(source="../../../assets/BasicBtnAssets2.swf", symbol="reg")]
		private var MyBtns1offState:Class;
		[Embed(source="../../../assets/BasicBtnAssets2.swf", symbol="ovr")]
		private var MyBtns1onState:Class;

		//create displayable object to attach graphics to
		private var _mybtn1off:Sprite;
		private var _mybtn1on:Sprite;
		
		public function BasicBtnAssets():void 
		{
			//attach graphic asset to displayable object	
			_mybtn1off = new MyBtns1offState();
			_mybtn1on = new MyBtns1onState();
		}
		
		/*  create getter method to give other classes a way to access 
			the displayable graphic object while preserving Encapsulation */						
		public function get mybtn1off():Sprite { return this._mybtn1off; }		
		public function get mybtn1on():Sprite { return this._mybtn1on; }		
	}	
}

Use the above graphic assets Class to display a 10 button menu on stage
Place TenOfSameButn.as in the same location as your main .fla file that uses this as its Document Class.

package 
{	
	import com.timshaya.model.BasicBtnAssets;
	
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.DisplayObject;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.MouseEvent;	
	
	[SWF (width="600", height="400", backgroundColor="#9999cc", frameRate="31")]
	public class TenOfSameButn extends MovieClip
	{				
		//a reference to the custom graphics object
		private var btnAssets:BasicBtnAssets; 
		private var butnGroup1:Sprite;
		
		//buttons
		private var butn1:      Sprite; //button 1 local container
		private var butn2:      Sprite;
		private var butn3:      Sprite;
		private var butn4:      Sprite;
		private var butn5:      Sprite; 
		private var butn6:      Sprite;
		private var butn7:      Sprite;
		private var butn8:      Sprite;
		private var butn9:      Sprite; 
		private var butn10:     Sprite; 		
		private var butnsArra:  Array = [butn1,butn2,butn3,butn4,butn5,butn6,butn7,butn8,butn9,butn10];
		
		private var offSet:     Number = 20; //horizontal button spacing offset
		
		public function TenOfSameButn():void 
		{						
			buildMenu();
		}		
		
		public function buildMenu():void 
		{
			butnGroup1 = new Sprite();
			addChild(butnGroup1);			
			
			for(var itm:int = 0; itm < butnsArra.length; itm++) //make 10 buttons
			{				
				butnsArra[itm] = new Sprite();
				//butnsArra[itm] = new MovieClip();																
				//butnsArra[itm].ID = itm; 
				//adding custom properties only works 
                                //with MovieClip, because it's a Dynamic Class															
																
				//create a new BasicBtnAssets instance for each button
				btnAssets = new BasicBtnAssets(); 				
				
				//put btn1 off state inside btn1 
				butnsArra[itm].addChild(btnAssets.mybtn1off);	
	
				//put btn1 on state inside btn1
				butnsArra[itm].addChild(btnAssets.mybtn1on);	
				
				//add each button to the group container so they can be repositioned as a group
				butnGroup1.addChild(butnsArra[itm]);		
				
				butnsArra[itm].x = 2.2;
				butnsArra[itm].x = butnsArra[itm].x * itm * offSet;
				//trace("butnsArra["+itm+"].x = " + butnsArra[itm].x);	
				butnsArra[itm].y = stage.stageHeight / 2 - butnsArra[itm].height/2 - 100;
				
				//hide Mouse Over state
				butnsArra[itm].getChildAt(1).visible = false; 
				butnsArra[itm].buttonMode = true;
				
				butnsArra[itm].addEventListener(MouseEvent.MOUSE_OVER, doMouseOvr, false, 0, true);
				butnsArra[itm].addEventListener(MouseEvent.MOUSE_OUT, doMouseOut, false, 0, true);				
			}
			
			//center btns container
			butnGroup1.x = stage.stageWidth/2 - butnGroup1.width/2;			
		}
		
		public function doMouseOvr(e:MouseEvent):void 
		{				
			e.target.parent.getChildAt(1).visible = true;
		}
		
		public function doMouseOut(e:MouseEvent):void 
		{
			e.target.parent.getChildAt(1).visible = false;
		}
	}	
}

Part 1: Using custom graphics via Flex SDK in the Flash IDE

Part 2. This is one way to organize graphical assets for an Actionscript-only AS3.0 project in Flash without using the Library.

Prep the files
In the Flash IDE* (CS4, for example), create an empty .fla and set Tester1 as the Document Class. Make sure you have an image named “mybutton1.png” inside a folder called /assets in your main directory, or in the same place your main .fla is located.

Create the graphic assets container Class
Place it inside this folder structure: {your main .fla’s location}/com/timshaya/model.

package com.timshaya.model
{	
	import flash.display.DisplayObject;
	
	public class BtnAssets 
	{	
		//embed graphic assets
		// "../../../" since "/assets" is outside this package
		[Embed("../../../assets/mybutton1.png")]
		private var MyBtn1:Class;
		
		//create displayable object to attach graphics to
		private var _mybtn1:DisplayObject;
		
		public function BtnAssets():void 
		{
			//attach graphic asset to displayable object	
			_mybtn1 = new MyBtn1();
		}
		
		/*  create getter method to give other classes a way to access 
			the displayable graphic object while preserving Encapsulation** */
		public function get mybtn1():DisplayObject { return this._mybtn1; }		
				
	}	
}

Use the above graphic assets Class to display a graphic on stage
Place Tester1.as in the same location as your main .fla file that uses this as its Document Class.

package 
{	
	import com.timshaya.model.BtnAssets;
	
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	
	[SWF (width="600", height="400", backgroundColor="#9999cc", frameRate="31")]
	public class Tester1 extends MovieClip
	{				
		//a reference to the custom graphics object
		private var btnAssets:BtnAssets; 

		//buttons
		private var butn1:DisplayObject;		
		
		public function Tester1():void 
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align=StageAlign.TOP_LEFT;
			
			btnAssets = new BtnAssets();
			
			//add button to stage
			butn1 = btnAssets.mybtn1;
			butn1.x = (stage.stageWidth/2) - butn1.width/2; //stage center
			butn1.y = stage.stageHeight/2 - butn1.height/2;
			addChild(butn1);			
		}		
	}	
}

Why bother? Why not just stuff all assets in the .fla via the Library? Doing it this way makes your AS3.0 code less IDE dependent. It’s easier to compile regardless of whether you’re using Flash, Flex Builder, FDT, or FlashDevelop.

* IDE means Integrated Development Environment. It’s a fancy way of saying “program” or “development tool.”

** Encapsulation is a key principle of OOP