September 2, 2009

ActionScript 3: Papervision3d: html links in a TextField

Papervision is fun, and the most commonly used 3D library in Flash. The developers have done an amazing job taking care of so many issues that arise when rendering multiple interactive objects to one BitmapData object. For example, if you create a 2D Sprite, put a button inside it somewhere, and then use that Sprite as a MovieMaterial texture for a 3D object, the MouseEvent listeners are automatically handled, and your buttons in the texture work as expected, which is awesome. One major issue along these lines is that if you have an html-enabled TextField with active hyperlinks, this button functionality does not get forwarded through the Papervision core. I saw some code that another developer had posted on a forum, and I rewrote it to work nicely with multiple links. The idea is that you can find the character positions of a hyperlink's text in the TextField, and draw buttons as Sprites in the 2D texture, on top of the TextField, so that your hyperlinks will have an active hit state and work inside a 3D object. I also included a dispose function to clean up, and have rollover listeners to enable the hand cursor in the PV3D viewport, which happens in a different class. This all could've been done a little cleaner with multiple classes and more regular expressions, but I wanted to keep it really simple and easy to implement and garbage collect. Here's the code:

protected var _htmlButtons:Array;
protected var _htmlButtonLinks:Array;

/**
* Create hit areas for html links - since they aren't handled automatically by PV
*/
protected function activateHrefs( textField:TextField ):void
{
var htmlTxtStr:String = textField.htmlText;
var plainTxtStr:String = textField.text;
var linkOpens:Array = getIndexesOfArray( htmlTxtStr, "<a " );
var linkCloses:Array = getIndexesOfArray( htmlTxtStr, "</a>" );

_htmlButtons = new Array();
_htmlButtonLinks = new Array();

// helps step through and not repeat duplicate links
var lastPlanTextIndex:int = 0;

// loop through links found
for( var i:int = 0; i < linkOpens.length; i++ )
{
// create button
var button:Sprite = new Sprite();
button.x = textField.x;
button.y = textField.y;
this.addChild( button );

// get text position in html text
var firstCharIndex:int = linkOpens[i];
var linkLength:int = linkCloses[i] - linkOpens[i] + 4;

// pull out string inside open and close tags
var linkString:String = htmlTxtStr.substr( firstCharIndex, linkLength );

// get href from <a> tag
var hrefPattern:RegExp = /href=['"]\S+['"]/i;
var hrefs:Array = linkString.match( hrefPattern );
var href:String = ( hrefs ) ? hrefs[0].substring(6, hrefs[0].length - 1) : "";

// strip tags
linkString = linkString.substr( linkString.indexOf( ">" ) + 1 ); // chop open tag
linkString = linkString.substr( 0, linkString.length - 4 ); // chop end tag

// find link text in non-html text
var linkStringPlainTextIndex:int = plainTxtStr.indexOf( linkString, lastPlanTextIndex );
lastPlanTextIndex = linkStringPlainTextIndex;

// draw rects for letters
button.graphics.beginFill(0xFF0000, 0);
for( var j:int = linkStringPlainTextIndex; j < linkStringPlainTextIndex + linkString.length; j++ )
{
var charRect:Rectangle = textField.getCharBoundaries(j);
if( charRect ) button.graphics.drawRect(charRect.x, charRect.y, charRect.width, charRect.height);
}
button.graphics.endFill();

// add listeners
button.addEventListener( MouseEvent.CLICK, onHyperlinkClick );
button.addEventListener( MouseEvent.MOUSE_OVER, onHtmlLinkOver );
button.addEventListener( MouseEvent.MOUSE_OUT, onHtmlLinkOut );

// store button and link so we can launch on click
_htmlButtons.push( button );
_htmlButtonLinks.push( href );
}
}

/**
* Returns an array of all the indexes of needle in haystack
*/
protected function getIndexesOfArray( haystack:String, needle:String ) : Array
{
var indexs:Array = new Array();
var startIndex:int = 0;
while( startIndex != -1 )
{
startIndex = haystack.indexOf( needle, startIndex );
if( startIndex != -1 )
{
indexs.push( startIndex );
startIndex += 1;
}
}
return indexs;
}

/**
* simply opens the link
*/
protected function onHyperlinkClick( e:MouseEvent ) : void
{
// find button and launch corresponding link
for( var i:int = 0; i < _htmlButtons.length; i++ )
{
if( e.target == _htmlButtons[i] )
{
navigateToURL( new URLRequest( _htmlButtonLinks[i] ), '_blank' );
}
}
}

protected function onHtmlLinkOver( e:MouseEvent ):void
{
// dispatch an Event to tell the PV3D viewport to enable the hand cursor:
// ( _pvView as BasicView).viewport.buttonMode = true;
}

protected function onHtmlLinkOut( e:MouseEvent ):void
{
// dispatch an Event to tell the PV3D viewport to disable the hand cursor:
// ( _pvView as BasicView).viewport.buttonMode = false;
}

/**
* clean up when if leave the papervision section
*/
public function dispose():void
{
// kill html hyperlink buttons
if(_htmlButtons != null) {
for( var i:int = 0; i < _htmlButtons.length; i++ )
{
_htmlButtons[i].removeEventListener( MouseEvent.CLICK, onHyperlinkClick );
_htmlButtons[i].removeEventListener( MouseEvent.MOUSE_OVER, onHtmlLinkOver );
_htmlButtons[i].removeEventListener( MouseEvent.MOUSE_OUT, onHtmlLinkOut );
}
_htmlButtons.splice( 0 );
_htmlButtonLinks.splice(0);
_htmlButtons = null;
_htmlButtonLinks = null;
}
}

September 1, 2009

ActionScript 3: Choose a random item from an Array, with weighting

It's very common to simply choose a random item from an Array, using a random number. But what if we want change the probabilities that certain items will be chosen? I've written a little function that takes an Array of weights (Numbers), which correspond to the Array of items you'd like to choose from, and returns an index to pull a random, weighted item from the source Array. Check out the example:

// our array of items
var fruits:Array = ['apple','orange','banana','mango'];
// our array of weights
var weights:Array = [20,10,40,30];
// pick a random fruit, based on weights, with bananas most likely to get picked
var myFruit:String = fruits[ randomIndexByWeights( weights ) ];

/**
* Takes an array of weights, and returns a random index based on the weights
*/
private function randomIndexByWeights( weights:Array ) : int
{
// add weights
var weightsTotal:Number = 0;
for( var i:int = 0; i < weights.length; i++ ) weightsTotal += weights[i];
// pick a random number in the total range
var rand:Number = Math.random() * weightsTotal;
// step through array to find where that would be
weightsTotal = 0;
for( i = 0; i < weights.length; i++ )
{
weightsTotal += weights[i];
if( rand < weightsTotal ) return i;
}
// if random num is exactly = weightsTotal
return weights.length - 1;
}

You can see that the weights array must be the same length as the data array that you're choosing from. Note that in the example, my weights add up to 100, but you can use any scale that you'd like, as the weights are added up, and a random number is chosen in that scale.

ActionScript 3: Shuffle/randomize any Array

A quick little function for randomizing/shuffling an Array that contains objects or primitive of any data type:

public static function randomizeArray( arr:Array ):void
{
for( var i:int = 0; i < arr.length; i++ )
{
var tmp:* = arr[i];
var randomIndex:int = Math.round( Math.random() * ( arr.length - 1 ) );
arr[i] = arr[randomIndex];
arr[randomIndex] = tmp;
}
}

You can see that it crawls through an Array, swapping each position with another, random position in the Array.

ActionScript 3: Adding a textual suffix to numbers

If you need to write out a number with a textual suffix, like "25th" or "173rd", there's a little logic that will make this really easy. Check it out:

// suffixes corresponding to the last digit of a number: 0-9
private static const NUMBER_SUFFIXES:Array = [ 'th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th' ];

private function getNumberSuffix( value : int ) : String
{
// handle most cases by modding by ten
var suffix:String = NUMBER_SUFFIXES[ value % 10 ];
if( value % 100 >= 11 && value % 100 <= 13 ) suffix = 'th'; // handle 11-13
if( value == 0 ) suffix = ''; // handle zero
return suffix;
}

August 31, 2009

ActionScript 3: Get the percentage of a value within a number range

Sometimes, when creating UI elements or performing geometric calculations based on the sizes or positions of objects (or lots of other tasks), it's useful to find the percentage of a number within a range of 2 other numbers. Use this function to easily relate these values:

/**
* Returns a percentage of a value in between 2 other numbers.
* @param bottomRange low end of the range.
* @param topRange top end of the range.
* @param valueInRange value to find a range percentage of.
* @return The percentage of valueInRange in the range.
* @use getPercentWithinRange( 50, 150, 100 ); // displays 50
*/
public static function getPercentWithinRange( bottomRange:Number, topRange:Number, valueInRange:Number ):Number
{
// normalize values to work off zero
if( bottomRange < 0 )
{
var addToAll:Number = Math.abs( bottomRange );
bottomRange += addToAll;
topRange += addToAll;
valueInRange += addToAll;
}
else if( bottomRange > 0 )
{
var subFromAll:Number = Math.abs( bottomRange );
bottomRange -= subFromAll;
topRange -= subFromAll;
valueInRange -= subFromAll;
}
// simple calc to get percentage
return 100 * ( valueInRange / ( topRange - bottomRange ) );
}

ActionScript 3: Constant easing function

Sometimes you want to ease a numeric variable or object property to a new value. Typically, you would do this with a tween, via the built-in Tween object, or a 3rd party tween library like Tweener or TweenLite/TweenMax. Sometimes, for a fluid interface, you might want different properties constantly easing (floating) towards their current target destination. In this situation, you would want to interpolate the values incrementally towards the target using the Event.ENTER_FRAME listener. I wrote a nice little function that will take the current value, the target value, and an optional easing value (where higher numbers increase the time to reach the target value), and ease the property towards the target. Check it out:

private function easeTo( current:Number, target:Number, easeFactor:Number = 10 ):Number
{
return current -= ( ( current - target ) / easeFactor );
}

// usage:
this.addEventListener( Event.ENTER_FRAME, onEnterFrameLoop );

private function onEnterFrameLoop( e:Event ):void {
mySprite.x = easeTo( mySprite.x, 200, 5 );
}

Now, wherever mySprite is, it will smoothly tween towards the x destination of 200. This can be applied to any numeric property.

June 18, 2009

Actionscript: Crossdomain issue when loading Twitter user icons

When building a new Flash piece, I ran into a crossdomain issue when trying to load the user icon images that come back from the Twitter API. After some searching, I found that by replacing the root of the url, the crossdomain issues are easily solved. Check it out:

var thumbnail:String = "http://s3.amazonaws.com/twitter_production/profile_images/252274568/profile_image_normal.jpg";
thumbnail = thumbnail.replace(/http:\/\/s3.amazonaws.com\/twitter_production\//g, 'http://twitter_production.s3.amazonaws.com/');

I was surprised it was this easy, but by moving the "twitter_production" portion of the path to the front of the subdomain, you now get a good crossdomain.xml here: http://twitter_production.s3.amazonaws.com/crossdomain.xml

May 13, 2009

Processing: Spiral growth generative art

A coworker got me excited to play around with Processing again, and I wrote a couple new generative art scripts. Here's a fun one:

float x;
float y;
float curRotation;
float rotationVelocity;
float curRadius;
float radiusVelocity;
float radiusThreshold;
float curSize;
float sizeVelocity;
float curRed;
float redVelocity;
float curGreen;
float greenVelocity;
float curBlue;
float blueVelocity;

void setup(){
size(1000,1000);
smooth();
background(45,0,0);
frameRate(100);
reset();
}

void draw(){

x = (width / 2) + sin(curRotation) * curRadius;
y = (height / 2) + cos(curRotation) * curRadius;

noStroke();
//stroke(100,100,100,150);
fill(curRed,curGreen,curBlue);
ellipse(x,y,curSize,curSize);

curRadius += radiusVelocity;
curRotation += rotationVelocity;
curSize -= sizeVelocity;
if( curRed > 45 ) curRed -= redVelocity;
curGreen -= greenVelocity;
curBlue -= blueVelocity;

if( curSize <= 0 ) //curRadius > radiusThreshold ||
{
reset();
}

}

void reset(){
curRadius = 0;
curRotation = random(0,3.14*2);
curSize = 20;
sizeVelocity = random(.05, .2);
rotationVelocity = random(.001, .009);
radiusVelocity = random( .1, 2 );
radiusThreshold = random( width / 3, width / 4 );
// make a shade of red
curRed = random(100, 255);
redVelocity = random( .1, 1 );
curGreen = random(0, 50);
greenVelocity = .5 * redVelocity;
curBlue = curGreen;
blueVelocity = greenVelocity;
}

It ends up looking something like this:

April 22, 2009

ActionScript 3: 502 error on crossdomain.xml request in IE

My team and I ran into a very strange security error today while testing our new Flash projects that load assets from external URLs. It only showed up in IE, which was quite mystifying. We had taken care of all of our crossdomain issues by updating our crossdomain.xml files, adding proper security code to all of our media loads as such:

new LoaderContext( true, ApplicationDomain.currentDomain, SecurityDomain.currentDomain );

But something wasn't right. I installed Fiddler, which is a super handy tool for debugging in IE, since FireBug is only available for FireFox. We found that IE was making the request for crossdomain.xml, but the server it was requesting from was the name of the media file, and not the actual server. After a bit of poking around we found that the CMS we were using was automatically formatting the Flash embed code, and was adding this:

<param value="" name="base">

This simple embed param caused the super weird behavior, only in IE. Hopefully this may be helpful to someone who runs into the same issue. Yay IE!

April 21, 2009

Javascript: scroll / animate a background image

I got the urge to add a little flavor to a repeating background image on one of my personal sites. When you roll over the div with your mouse, the image starts scrolling by incrementing the background-position css. First you need a div with a background-image, a background-position, and an id of "header":

#header {
background-image:url(../images/site-header-single.gif);
background-position:-73px 0;
background-repeat:repeat-x;
width:752px;
height:242px;
}

Then you need to attach the mouse rollover functionality to the header - I did this dynamically with protoculous, as seen below in the final Event.observe method. The rest is simple: start a timer, grab the background position offset from the css, increment, and apply the reassembled css. Easy:

// setTimeout variable for clearing on rollout
var headerTimeout;

function addHeaderBehavior() {
Event.observe( $('header'), 'mouseover', startHeaderAnim, false);
Event.observe( $('header'), 'mouseout', stopHeaderAnim, false);
}

function startHeaderAnim() {
headerTimeout = setTimeout( "incrementHeader()", 50 );
}

function incrementHeader()
{
// get x variable of background-position
var bgOffsets = $('header').getStyle('background-position').split(' ');
var offsetX = bgOffsets[0];
var offsetY = bgOffsets[1];
// strip px and increment
offsetX = offsetX.replace( /px/, '' );
offsetX = parseInt( offsetX ) + 1;
// update style
$('header').setStyle( { backgroundPosition:offsetX + 'px ' + offsetY } );

startHeaderAnim();
}

function stopHeaderAnim(event) {
clearTimeout( headerTimeout );
}


Event.observe(window, 'load', addHeaderBehavior, false);

April 2, 2009

ActionScript 3: Easily generate code to draw a complex image

In Flash, I always do everything in a programmatic way if possible. This includes drawing shapes with code instead of having library items. This is especially true, now that the preferred method of compiling is using the Flex mxmlc compiler. Sometimes, though, certain things are too much of a pain to draw pixel by pixel. No longer though, as there's a REALLY simple way to generate the code to draw complex shapes for you.

Step 1: save a .png of the image you want drawn.
Step 2: open Flash, and drop your image into the library, giving it a linkage ID of "MyGraphic".
Step 3: drop the following code into the actions panel and publish.
Step 4: copy the output code and apply it to a BitmapData object in your project.

var mc : MyGraphic = new MyGraphic( null, null );
for( var i:int = 0; i < mc.width; i ++)
{
for( var j:int = 0; j < mc.height; j ++)
{
if( mc.getPixel32( i, j ) != 0 ) trace( "bmp.setPixel32( "+i+", "+j+", "+mc.getPixel32( i, j )+" );" );
}
}



the resulting code in my case looked like this:

public static function drawVideoPlayhead():Bitmap
{
var bitmap:Bitmap = new Bitmap();
var bmp:BitmapData = new BitmapData( 7, 8, true, 0x00000000 );

// begin generated code
bmp.setPixel32( 0, 3, 4289309097 );
bmp.setPixel32( 0, 4, 4288322202 );
bmp.setPixel32( 0, 5, 4287664272 );
bmp.setPixel32( 0, 6, 4286874756 );
bmp.setPixel32( 1, 2, 4290888129 );
bmp.setPixel32( 1, 3, 4291085508 );
bmp.setPixel32( 1, 4, 4290822336 );
bmp.setPixel32( 1, 5, 4290032820 );
bmp.setPixel32( 1, 6, 4288256409 );
bmp.setPixel32( 1, 7, 4286874756 );
bmp.setPixel32( 2, 1, 4291677645 );
bmp.setPixel32( 2, 2, 4291875024 );
bmp.setPixel32( 2, 3, 4291677645 );
bmp.setPixel32( 2, 4, 4290822336 );
bmp.setPixel32( 2, 5, 4290032820 );
bmp.setPixel32( 2, 6, 4289374890 );
bmp.setPixel32( 2, 7, 4287598479 );
bmp.setPixel32( 3, 0, 4293322470 );
bmp.setPixel32( 3, 1, 4293388263 );
bmp.setPixel32( 3, 2, 4292335575 );
bmp.setPixel32( 3, 3, 4291677645 );
bmp.setPixel32( 3, 4, 4290822336 );
bmp.setPixel32( 3, 5, 4290032820 );
bmp.setPixel32( 3, 6, 4289374890 );
bmp.setPixel32( 3, 7, 4287598479 );
bmp.setPixel32( 4, 1, 4294046193 );
bmp.setPixel32( 4, 2, 4293256677 );
bmp.setPixel32( 4, 3, 4291677645 );
bmp.setPixel32( 4, 4, 4290822336 );
bmp.setPixel32( 4, 5, 4290032820 );
bmp.setPixel32( 4, 6, 4289374890 );
bmp.setPixel32( 4, 7, 4287598479 );
bmp.setPixel32( 5, 2, 4293717228 );
bmp.setPixel32( 5, 3, 4292861919 );
bmp.setPixel32( 5, 4, 4290822336 );
bmp.setPixel32( 5, 5, 4290032820 );
bmp.setPixel32( 5, 6, 4289703855 );
bmp.setPixel32( 5, 7, 4288059030 );
bmp.setPixel32( 6, 3, 4293914607 );
bmp.setPixel32( 6, 4, 4293190884 );
bmp.setPixel32( 6, 5, 4292730333 );
bmp.setPixel32( 6, 6, 4291677645 );
// end generated code

bitmap.bitmapData = bmp;
return bitmap;
}


Notes: using the setPixel32() function, and defaulting the BitmapData to a background color of 0x00000000 ensures that your Bitmap will have a transparent background. Note that the code here ignores empty pixels for efficiency. Finally, this code could easily be converted to draw into a Shape or Sprite, but for my purposes, bitmaps are usually what I want. Hope you find this useful!

March 16, 2009

ActionScript 3: Toggling a Boolean the easy way

This may be common knowledge to some, but I was pretty excited when I realized I didn't need an conditional statement to figure out which direction to toggle a Boolean value. Check it out:

var isTrue:Boolean = true;
isTrue = !isTrue;
trace(isTrue); // prints "false"
isTrue = !isTrue;
trace(isTrue); // prints "true"

Simplicity is your friend.

March 9, 2009

ActionScript 3: Checking for empty xml nodes

When parsing xml in AS3, you grab a leaf node's inner content by using the XML( node ).text() function. You can compare the result with a String, but if you're checking for an empty string, the .text() function doesn't compute, because it returns an XMLList, instead of a String. While a non-empty string comparison works fine between an XMLList and a String, an empty String ( i.e. var mystring:String = ""; ) is not the same as an empty XMLList. To ensure that your comparison works, it's always a good policy to cast your text node to a String. See the example below:

var isEmptyNode:Boolean = false;
if( String( node["myNodeName"].text() ) == "" )
{
isEmptyNode = true;
}

February 4, 2009

iPhone: MPMoviePlayerController preload issues

As of this writing, the MPMoviePlayerController object doesn't seem to send the MPMoviePlayerContentPreloadDidFinishNotification notification on the iPhone Simulator. This is happening when I'm loading an .m4v over the net. This issue makes my app appear broken on the simulator, because I'm waiting for the preload to finish before calling the "play" function on the MPMoviePlayerController object. When I test on the iPhone itself, there's no problem. Just an FYI.

January 30, 2009

iPhone: Rotate a UIImageView

Here's a simple little snippet that's useful if you want to rotate a UIImageView continuously for something like a clock hand graphic:

#define DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) / 180.0 * M_PI)

CGAffineTransform cgCTM = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(45));
myImage.transform = cgCTM;

iPhone: Custom font loading : complete example

UPDATE: It looks like someone has built a far more robust custom font engine than my example here. I haven't tried it yet, but the example project looks great: http://github.com/zynga/FontLabel

This code was cobbled together from a couple different posts I found on blogs and iphone forums, here and here. This is a complete example, which nobody wanted to supply - I hope this is useful to someone. The one major hole is that is does not support multi-line text. Let me know if you figure out a good way to handle it :)

I'm just going to post my 2 classes here. I've set up a base class that does the drawing, and a subclass that defines font-specific configuration. You'll have to put your .ttf (TrueType) font in the bundle, and use it's name there to customize your own font subclass. You'll also have to set the glyph offset in the subclass - a font-editing tool can help with this.

Here we go:

CustomFontBase.h
@interface CustomFontBase : UIView {
NSMutableString *curText;
UIColor *fontColor;
UIColor *bgColor;
int fontSize;
NSString *fontName;
NSString *fontExtension;
float autoSizeWidth;
int glyphOffset;
BOOL isGlowing;
UIColor *glowColor;
}

- (void)updateText:(NSString*)newText;
- (void)initTextWithSize:(float)size color:(UIColor*)color bgColor:(UIColor*)bgColor;
- (void)setGlow:(BOOL)glowing withColor:(UIColor*)color;
- (void)autoSizeWidthNow;

@property (nonatomic, retain) NSMutableString *curText;

@end

CustomFontBase.m
#import "CustomFontBase.h"


@implementation CustomFontBase

@synthesize curText;


- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// set defaults
[self setBackgroundColor:[UIColor clearColor]];
bgColor = [UIColor clearColor];
[self setCurText: [[NSMutableString alloc] initWithString:@""] ];
fontColor = [UIColor whiteColor];
fontSize = 15;
isGlowing = FALSE;
[self setContentMode:UIViewContentModeTopLeft];  // make sure it doesn't scale/deform when setFrame is called 
}
return self;
}


- (void)drawRect:(CGRect)rect {
// get context and flip for normal coordinates
CGContextRef context =  UIGraphicsGetCurrentContext();
CGContextTranslateCTM ( context, 0, self.bounds.size.height );
CGContextScaleCTM ( context, 1.0, -1.0 );

// Get the path to our custom font and create a data provider.
NSString *fontPath = [[NSBundle mainBundle] pathForResource:fontName ofType:fontExtension];
CGDataProviderRef fontDataProvider = CGDataProviderCreateWithFilename([fontPath UTF8String]);
// Create the font with the data provider, then release the data provider.
CGFontRef customFont = CGFontCreateWithDataProvider(fontDataProvider);
CGDataProviderRelease(fontDataProvider); 
// Set the customFont to be the font used to draw.
CGContextSetFont(context, customFont);

// prepare characters for printing
NSString *theText = [NSString stringWithString: curText];
int length = [theText length];
unichar chars[length];
CGGlyph glyphs[length];
[theText getCharacters:chars range:NSMakeRange(0, length)];

// draw bg
if( bgColor != [UIColor clearColor] )
{
CGRect bgRect = CGRectMake (0, 0, self.bounds.size.width, self.bounds.size.height);
CGContextSetFillColorWithColor( context, bgColor.CGColor );
CGContextFillRect( context, bgRect );
}

// Set how the context draws the font, what color, how big.
CGContextSetTextDrawingMode(context, kCGTextFill);
CGContextSetFillColorWithColor(context, fontColor.CGColor );
CGContextSetFontSize(context, fontSize);

// set a glow?
if( isGlowing ) {
//CGContextSetShadow(context, CGSizeMake(0,0), 3 );
CGContextSetShadowWithColor( context, CGSizeMake(0,0), 3, glowColor.CGColor );
}

// Loop through the entire length of the text.
for (int i = 0; i < length; ++i) {
// Store each letter in a Glyph and subtract the MagicNumber to get appropriate value.
glyphs[i] = [theText characterAtIndex:i] + glyphOffset;
}

// draw the glyphs
CGContextShowGlyphsAtPoint( context, 0, 0 + fontSize * .25, glyphs, length ); // hack the y-point to make sure it's not cut off below font baseline - this creates a perfect vertical fit

// get width of text for autosizing the frame later (perhaps)
CGPoint textEnd = CGContextGetTextPosition( context ); 
autoSizeWidth = textEnd.x;

// clean up the font
CGFontRelease( customFont );
}


// call this after creating the LandscapeText object to set the styling
- (void)initTextWithSize:(float)size color:(UIColor*)color bgColor:(UIColor*)txtBgColor {
// store font properties
fontColor = color;
fontSize = size;
bgColor = txtBgColor;

// autoscale height to font size
[self setFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, fontSize)];
}

// set new text to display
- (void)updateText:(NSString*)newText {
[self setCurText: [NSString stringWithString:newText] ];
[self setNeedsDisplay];
}

- (void)setGlow:(BOOL)glowing withColor:(UIColor*)color {
glowColor = color;
isGlowing = glowing;
}

- (void)autoSizeWidthNow {
//printf( "autoSizeWidth = %f \n", autoSizeWidth );
[self setFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, autoSizeWidth, fontSize)];
}


- (void)dealloc {
[curText release];
[super dealloc];
}


@end
CustomFontMyFont.h
#import 
#import "CustomFontBase.h"

@interface CustomFontMyFont : CustomFontBase {

}

@end
CustomFontMyFont.m
#import "CustomFontMyFont.h"


@implementation CustomFontMyFont


- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
fontName = @"MyFont";
fontExtension = @"ttf";
glyphOffset = -29;        // adjust this offset per font until it prints the proper characters
}
return self;
}


- (void)dealloc {
[super dealloc];
}


@end
Usage:
CustomFontMyFont *myLabel = [[[CustomFontMyFont alloc] initWithFrame:CGRectMake(100, 100, 55, 20)] autorelease];
[myLabel initTextWithSize:11 color:[UIColor whiteColor] bgColor:[UIColor clearColor]];
[myLabel updateText:@"Custom Font"];
[self addSubview:myLabel];

January 29, 2009

iPhone: Attach an image with one line of code

I got sick of the 4 lines of code it takes to attach and garbage collect a UIImageView to your UIView, so I wrote a static function for our DisplayUtil class to clean up this process. Check it out below and feel free to use:

+ (UIImageView*)attachImageToView:(UIView*)viewObject withId:(NSString*)imgId andRect:(CGRect)rect {
UIImageView *img = [[UIImageView alloc] initWithFrame:rect];
img.image = [UIImage imageNamed:imgId];
[viewObject addSubview:img];
[img release];
return img;
}

January 27, 2009

ActionScript 3: Prevent graphics from blocking your button

In Flash 9, a non-button graphic that lays over a real button wil prevent the user from clicking the button underneath. This was never a problem in Flash before AS3 / Flash 9, but is quite a common problem to deal with. It's quite easy to get around the issue. You simply need to disable any mouse interaction with the inactive graphic. Even though you haven't told it to act as a button, the default is to capture mouse actions as a DisplayObject. Just add this code to the non-button graphic:

graphic.mouseChildren = false;
graphic.mouseEnabled = false;

Fixed!

January 21, 2009

iPhone: Disable screen dimming

This can be very handy if you're developing an iPhone game, where the screen shouldn't automatically dim to save battery life. Use this code when you launch your gameplay view, and set it back when you leave gameplay:

[[UIApplication sharedApplication] setIdleTimerDisabled:YES];

January 19, 2009

iPhone: send and receive events/notifications

Objective-C has a really friendly and useful event system that allows your objects to talk to each other without any knowledge of the other objects. Events are called Notifications in Objective-C, and are routed through a system-wide object called NSNotificationCenter. You don't need to import this object - it's always available to every class.

Here's the syntax for dispatching an event/notification with an ID of "EventName":

[[NSNotificationCenter defaultCenter] postNotificationName:"EventName" object:self];

It also sends along "self", which is a reference to the dispatching object. You could replace "self" with a reference to another object that may contain more relevant data to the event being dispatched.

To respond to this event, you need to set up a listener:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(listenerFunction:) name:@"EventName" object:nil];

Our object, "self" is now ready to receive this event. It must have a function called "listenerFunction", but this name is up to you. Note that setting "nil" as the object lets us respond to any object in the application that sends out a notification called "EventName". Alternatively, we can listen to only one object that will send this event, by replacing "nil" with a reference to the object.

Finally, you need a function that actually does something when the event/notification is received. See below:

- (void)listenerFunction:(NSNotification *)notification
{
MyDispactherObject *incomingObject = [notification object];
NSLog(@"EVENT RECEIVED");
}

If we don't need to do anything with the object that was sent with the event, we can ignore the first line in the function.

[UPDATE]: Thanks to CC in the comment section for correcting my example and reminding us that we want to clean up after ourselves and remove event listeners before things are properly garbage collected. Here's the syntax for removing the listener we added above (replace "nil" with the object reference if you were listening to a specific object):

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"EventName" object:nil];


Finally, here's Apple's official Notification documentation for further reading:
http://developer.apple.com/documentation/Cocoa/Conceptual/Notifications/Introduction/introNotifications.html

January 16, 2009

ActionScript: Random number function

I saw that one of the top results for "actionscript random number" in a Google search is incorrect, so I figured it would be a good idea to post another proper one.

Here's the ActionScript 3 version:
function randNum( low:int, high:int ):int
{
return Math.round( Math.random() * (high - low) ) + low;
}

And here's the ActionScript 2 version:
function randNum( low:Number, high:Number ):Number
{
return Math.round( Math.random() * (high - low) ) + low;
}

January 15, 2009

Javascript: setInterval() - call a function every X seconds

setInterval() is used when you want a function to run repeatedly on a timer. I wrote this little html page to remind me to get up and stretch every half-hour. I keep it open in my browser during the day to keep me from slouching for too long. Programming for hours on end can lead to Repetitive Strain Injuries in the hands and arms (amongst other problems), so this could help you too :)

<html>
<head>
<script>
setInterval ( "tellMeToGetUp()", 1000 * 60 * 30 );
function tellMeToGetUp()
{
alert('GET UP DUDE!');
}
</script>
</head>
<body>
</body>
</html>
Note the time passed into the setInterval function. It means: 1000 milliseconds x 60 seconds x 30 minutes. setInterval uses milliseconds, so this is a clear way to describe how long you want the interval to be.

January 14, 2009

iPhone: Rotate a sub view to landscape layout

After trying some other hacks, I came up with this little bit of code to rotate my full-screen UIView to landscape mode, which let me lay out all the sub views in a standard coordinate system.

#define DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) / 180.0 * M_PI)

- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
self.backgroundColor = [UIColor clearColor];
[self setViewToLandscape:self];
}
return self;
}

-(void)setViewToLandscape:(UIView*)viewObject {
[viewObject setCenter:CGPointMake(160, 240)];
CGAffineTransform cgCTM = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(-90));
viewObject.transform = cgCTM;
viewObject.bounds = CGRectMake(0, 0, 480, 320);
}

January 13, 2009

iPhone: Create a UIButton with a custom graphic

Here's a little code snippet if you don't want to use the standard UIButton styles. You can attach an image instead:
UIButton *button = [[UIButton buttonWithType:UIButtonTypeCustom] initWithFrame:CGRectMake(0, 0, 24, 24)];
[button addTarget:self action:@selector(prevButtonClick:) forControlEvents:UIControlEventTouchUpInside];
[button setBackgroundImage:[UIImage imageNamed:@"IntroArrowLeft.png"] forState:UIControlStateNormal];
[self addSubview:button];

January 12, 2009

iPhone: Create a UIButton and respond to the touch

Here's the basic code to create a text button and do something when it's touched:

// add the button to the view
-(void)buildButton {
UIButton *button = [[UIButton buttonWithType:UIButtonTypeRoundedRect] initWithFrame:CGRectMake(0, 0, 150, 25)];
[button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"CLICK IT" forState:UIControlStateNormal];
[button setCenter:CGPointMake( 320 / 2, 480 - 25 )];
[self addSubview:button];
}

// respond to the button click
-(void)buttonClick:(UIView*)clickedButton
{
NSLog(@"CLICK!");
}