Thursday, November 11, 2010

iPhone: Custom font loading : complete example


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];

No comments:

Post a Comment

Followers