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!");
}