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]; } @endCustomFontMyFont.h
#importCustomFontMyFont.m#import "CustomFontBase.h" @interface CustomFontMyFont : CustomFontBase { } @end
#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]; } @endUsage:
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];
Hey,
ReplyDeleteThanks for the code :D
How can I use this code in a UITableView/Cell and a UILabel?
Thanks.
It's a UIView subclass, so use it just like any normal UIView.
ReplyDeleteThanks Justin. Is there a proper way to use it? I'm tried doing it but it crashes. I'm trying make the text in a tableview and UILabel use that custom font, how can I do that?
ReplyDeleteI added to the bottom of my post, with the code to create a text field. you have to create the textfield with the code above. this does not work as a UILabel. I can't give much more advice than that.
ReplyDeleteThanks Justin :)
ReplyDeleteJustin, thanks a lot! You saved my nerves )
ReplyDeleteHow do you use the autoSizeWidthNow method?
ReplyDeleteOh, and by the way: is it possible to let CG draw centered text?
ReplyDeleteYou call the autoSizeWidthNow simply by sending that message to your new custom font text field:
ReplyDelete[myLabel autoSizeWidthNow];
This will allow you to center or right-align the text as it adjusts the frame of your text field. The only problem is that you can't seem to create a text field and call the autoSizeWidthNow function on the same frame. I had to set up a timer to call autoSizeWidthNow a fraction of a second later, because objective-c doesn't seems to recognize resized UIView frame/bounds dimensions until the next frame. If anyone has any insight into that, I'd be interested to learn. It seems like there should be something like setNeedsDisplay, but for UIView elements, not just the CG drawing layer. Hope that helps explain my code!
Thanks for the post. It's easier to learn from
ReplyDeletecode than from documentation.
I'll return a couple of clues: that declaration
for the glyphs array needs a constant at compile
time, not a runtime variable for the array size;
and that chars array is unused in your example.
And if you open Font Book, click on Edit>>Special
Charcters, and select your font, you'll see part
of the reason for the glyphoffset variable.
That last portion of code that you added, where does it go? I got everything else done and it returns a blank screen. I assume that that last part is the part that actually does stuff. Would you consider emailing the files? If not, I understand but I have been searching for how to do this FOREVER and can't get it to work. Thanks!
ReplyDeletethe Usage code goes in any UIView subclass where you're building a view with code. You need to import your custom font class, then use the Usage code where you're building the view. All the code you need is provided here. take note that I don't use Interface Builder. everything I create is built entirely with code, so if you're using IB, I can't give good advice.
ReplyDeleteWould you mind looking at my code to see what is wrong. This still isn't working. The file can be found here: http://cdreich.com/MyCode.html
ReplyDeleteThank you sooo much for all of your help. I am sorry if I am a bother.
Thanks! This solve all of my problems :)
ReplyDeleteOMG U R00lZ d00d
ReplyDeleteThanks!
ReplyDeleteI found a leaking object in CustomFontBase.m
Replace:
[self setCurText: [[NSMutableString alloc] initWithString:@""]];
with:
curText = [[NSMutableString alloc] initWithString:@""];
and all should be good
Hey This class is great, seems to work fine, The only problem I have run into is that when you put the custom font inside of each cell of a table view, it will scroll down fine, but as soon as you scroll the table view up, it crashes with this error.
ReplyDeleteNSInvalidArgumentException, [RootViewController updateText:] unrecognized selector sent to class 0x84a0
any ideas? thanks!
This code works fine if you only need ASCII, no unicode or extended characters. For a unicode solution that's mostly similar except that it reads the font's cmap to find the glyphs, registered developers should consult this thread
ReplyDeletehttps://devforums.apple.com/message/113783
if i use in a for cycle:
ReplyDelete[myLabel updateText:[my_array objectAtIndex:i]];
The label return not a string but a number
myArray = [[NSArray alloc] initWithObjects:
@"111",
@"222",
@"333",
@"444",
@"555",
nil];
Return
001,002,003,004,005
Why?
Any solution?
Thz.
So I wast using this code got most stuff to work, cept when I try to use say
ReplyDelete[myLabel initTextWithSize:11 color:[UIColor colorWithRed:...] bgColor:[UIColor colorWithRed:...]
I'm not really sure why not? I've changed the CGSetContextFillWithColor method in the CustomFontMyFont class too specify a [UIColor ..].CGColor and that works, but don't know why I can't pass it through initWithText?
I am using the code exactly as presented, but getting a leak on this line in initWithFrame:
ReplyDelete[self setCurText: [[NSMutableString alloc] initWithString:@""] ];
By doing "Build and Analyze" I get this report:
"Potential leak of an object on line 26" (the setCurText line above is line 26)
Anyone else see this? Any fixes that can correct it?
Thanks!
I wanted to have custom fonts, cause I habe Chinese text and I wanted nice chinese signs (like when you draw with a brush).
ReplyDeleteBut it seems that it doesn't work with chinese fonts. Maybe you know why? I want to print this f.e.: ä½
And I would like to use a font like HDZB_36.TTF (first result in google)
Justin, this is great. I am not a programmer but a font developer. Would you be interested to talk about bringing our fonts to iPhone? Please write me at peter at typotheque dot com
ReplyDeletehow could i use it for UIWebview's default font?
ReplyDeleteany good tool to find the glyh offset ?
ReplyDeleteGreat helpful code!
ReplyDeleteBut I'm stuck trying to right align the text. Could anyone please point it out please????
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
ReplyDeleteThanks to all who have used my code, and sorry for not replying to all the questions.
Hi Justin,
ReplyDeleteI am trying to use above in project and just added above classes, and then added customfontmyfont view to a viewcontroller, and my controller is not showing it up.. your little help on this would be really appreciated.. thanks a ton..
Hi Justin ,
ReplyDeletei want to use custom fonts with UITextView .. Please help !
Thanks
Yasir
Great Classes very helpful, especially for portability.
ReplyDeleteIf people are loading the text to screen via viewDidLoad method use this:
[self.view addSubview:myTitle];
Instead of:
[self addSubview:myTitle];
Hope that helps some newbie programmers.
Nome Skavinski.