Custom Fonts on the iPad and iOS 4

In the old days (you know, before the iPad and iOS 4), text layout in iOS was not for the faint of heart. A developer who wanted more than UILabel could provide was forced to manually layout and draw text with Quartz. Those days are gone now thanks to Core Text and it sibling CATextLayer.

Core Text could fill an entire book on its own. Luckily, most of Core Text is only necessary for about 5% of iOS apps. Most of us do not need to layout large amounts of text with complex style attributes. Sometimes we just want to use our own custom font or italicize one word in a label. There is a solution for the rest of us: CATextLayer. Like its counterparts in the Core Animation framework, CATextLayer allows us to take advantage of a complex API (Core Text in this case) without much effort.

We will cover a simple use of CATextLayer this time: displaying a custom font. As always, I have added more code to my CA360 project. Check it out on github to follow along.

Displaying Custom Fonts

Using CATextLayer to display a custom font is fairly simple. There are two things we must do to make it work:

  • Load the font into memory (most likely from our app bundle).
  • Hand the font to our CATextLayer for drawing.

Once the font has been passed to our CATextLayer instance, we can set the string of the layer and treat it like any other Core Animation layer. Let’s see how this looks in code.

Load the Font into Memory

Because CATextLayer uses Core Text internally, we’ll need to touch a little of Core Text to render our custom font. Like Quartz, Core Text is a Core Foundation API, and it is entirely C based. If you’ve done any custom drawing with Quartz, the Core Text API will look familiar. If this is your first foray into a Core Foundation framework, don’t worry, CATextLayer abstracts most of Core Text away. You still might want to take a look at the Apple Core Foundation docs if this part is way over your head.

CATextLayer expects a CTFontRef (CT is the prefix for all Core Text types and functions) for its font attribute. This is where we have to use Core Text directly.

We actually go about this in a bit of a roundabout way. Core Text does not know how to load a font from a font file. That’s the domain of Qaurtz. So we have to load the font file with Quartz and convert the CGFontRef to a CTFontRef. Thankfully, this conversion is just a single function call.

- (CTFontRef)newCustomFontWithName:(NSString *)fontName
                            ofType:(NSString *)type
                        attributes:(NSDictionary *)attributes
{
  NSString *fontPath = [[NSBundle mainBundle] pathForResource:fontName ofType:type];

  NSData *data = [[NSData alloc] initWithContentsOfFile:fontPath];
  CGDataProviderRef fontProvider = CGDataProviderCreateWithCFData((CFDataRef)data);
  [data release];

  CGFontRef cgFont = CGFontCreateWithDataProvider(fontProvider);
  CGDataProviderRelease(fontProvider);

  CTFontDescriptorRef fontDescriptor = CTFontDescriptorCreateWithAttributes((CFDictionaryRef)attributes);
  CTFontRef font = CTFontCreateWithGraphicsFont(cgFont, 0, NULL, fontDescriptor);
  CFRelease(fontDescriptor);
  CGFontRelease(cgFont);
  return font;
}

As you can see, we use a CGDataProvider to load the font from the app bundle and create a CGFontRef with the data provider.

Next, we create at CTFontDescriptorRef. CTFont is very configurable, and the CTFontDescriptor holds all of that configuration information. We’ll get to it in a later post. You can ignore the CTFontDescriptor for now.

The last thing we do in this method is create the CTFontRef using our CGFontRef and the CTFontDescriptorRef. Now, our font is loaded and ready to be used.

Configure the CATextLayer

CATextLayer could not be easier to configure. Like other CALayer objects, the CATextLayer exposes a set of attributes that we can set to configure the look of our text. Since we loaded the font earlier, we can just set the font attribute of CATextLayer, and the layer handles the rest.

normalTextLayer_ = [[CATextLayer alloc] init];
normalTextLayer_.font = font;
normalTextLayer_.string = @"This is just a plain old CATextLayer";
normalTextLayer_.wrapped = YES;
normalTextLayer_.foregroundColor = [[UIColor purpleColor] CGColor];
normalTextLayer_.fontSize = 20.f;
normalTextLayer_.alignmentMode = kCAAlignmentCenter;
normalTextLayer_.frame = CGRectMake(0.f, 10.f, 320.f, 32.f);

As you can see, there are a bunch of configuration options for CATextLayer. You can see them all in the class reference doc. Once we’ve finished setting up our layer, we add it to the layer tree, and Core Animation will render it. The final product looks something like this:

http://i2.wp.com/www.freetimestudios.com/wp-content/uploads/2010/09/CATextLayer-Simple.png

Now, Unleash the Designers

Thanks to the updates in iOS 4, developers are no longer stuck with the fonts built into iOS. Good typography can really elevate the look of an app, and I encourage you to experiment with quality fonts. Let you designers run wild. They’ll love you for it.

19 Responses to “Custom Fonts on the iPad and iOS 4”

  1. Hans Pinckaers September 13, 2010 at 5:12 am #

    You can also add the font filename to your plist and use fontWithName: don't now if it works on iPad yet.

    • Ilari Sani September 13, 2010 at 6:53 am #

      To be more specific, add to your Info.plist an array of font file names with the key UIAppFonts. This makes the fonts available throughout the app, even including UIWebView. Works on iOS 3.2, so it's iPad compatible.

      • neror September 13, 2010 at 4:07 pm #

        Yes. I remember something about that from WWDC. That's definitely simpler than my method, and I need to add it to the sample. It's still good to know how to manually load a font in case you can't supply the font in your app bundle for some reason.

        I need to watch that WWDC video again.

        Thanks Hans and Ilari.

        • Ankur November 3, 2010 at 8:44 pm #

          which video was this?

          and thanks for this blog post!

          • Nathan Eror November 3, 2010 at 11:09 pm #

            Session 110 – Advanced Text Handling for iPhone OS.

            And you're welcome. :)

  2. Anonymous September 13, 2010 at 3:26 pm #

    Good read, thanks for the article!

    I'm curious what format the font file needs to be in, for either the CoreText technique in the article, or the NSFont fontWithName: technique mentioned in the comments. I ask because I'm working with a designer who is using custom fonts, which they sent to me as extension-less files that Finder is reporting are of kind "Font Suitcase" and "PostScript Type 1 outline font." All of the documentation I have found calls for fonts with a .TTF extension, and I haven't been able to get the font files I have to work.

    I'm fairly ignorant of fonts and font files, so forgive me if the above makes little sense.

    • neror September 13, 2010 at 4:04 pm #

      I'm not a font expert myself, but I know Core Text supports PostScript fonts. I've only dealt with TrueType and OpenType fonts myself.

      The CTFontDescriptor holds a wealth of information about the font including its format. The CTFontFormat enum in CTFontDescriptor.h should give you an idea of what's supported:

      enum {
      kCTFontFormatUnrecognized = 0,
      kCTFontFormatOpenTypePostScript = 1,
      kCTFontFormatOpenTypeTrueType = 2,
      kCTFontFormatTrueType = 3,
      kCTFontFormatPostScript = 4,
      kCTFontFormatBitmap = 5
      };
      typedef uint32_t CTFontFormat;

      I've found the font system to be pretty forgiving. I pulled the kCTFontFormatAttribute out of the custom font in my example, and it came back as "unknown". The font still renders correctly, though.

      Here's the code to do that, BTW:

      CTFontDescriptorRef desc = CTFontCopyFontDescriptor(font);
      CFNumberRef format = CTFontDescriptorCopyAttribute(desc, kCTFontFormatAttribute);
      int formatId;
      CFNumberGetValue(format, kCFNumberSInt32Type, &formatId);
      NSLog(@"font type: %d", formatId);
      CFRelease(desc);
      CFRelease(format);

      Also, Core Text intelligently falls back to other fonts when it can't use the one you've provided. It takes multiple font attributes into account when it chooses a replacement, and it does a pretty decent job.

  3. Joe Nash September 16, 2010 at 5:30 am #

    This looks really useful, just what we need for our project! However, you have an "attributes" parameter which takes an NSDictionary… How do you use this to set the font size and other styles?

    Thanks! :-)

    • Nathan Eror September 17, 2010 at 1:33 pm #

      The attributes dictionary is used to create the CTFontDescriptor in the newCustomFontWithName:ofType:attributes: method. The possible attributes are enumerated in the docs here: http://bit.ly/ctfontdescriptorattrsI'll also be expanding on this stuff more in future posts.Thanks for reading!

  4. Jon September 17, 2010 at 2:44 pm #

    Hey Nathan –

    Thanks for the article, this is an interesting subject that's complicated by some pretty confusing documentation. I'm banging my head against the wall with CTFontDescriptorCreateWithAttributes as what you put in the dictionary gets a bit complicated and t'internet is devoid of examples in this area! Having real problems on how to set the font name (via kCTFontDisplayNameAttribute? kCTFontNameAttribute? kCTFontStyleNameAttribute? Any of these, all of them? I've tried all combinations!!!?)

    Some sample code on non-trivial use of CTFontDescriptorCreateWithAttributes would be fantastic!!!!

  5. Anonymous October 20, 2010 at 9:31 am #

    I tried implementing the tutorial above and get the following error. I cannot understand for the life of me what it means.

    Font Name: Intellect.ttf

    ] CoreText: Invalid 'kern' Subtable In CTFont
    CTFontDescriptor <attributes: {type = mutable, count = 1, capacity = 3, pairs = (
    0 : {contents = "NSFontNameAttribute"} = {contents = "Intellect"}

    • Nathan Eror October 21, 2010 at 1:25 pm #

      It looks like you have a corrupt ttf font file. I assume this happens when you try to load the font in your app.

      You can validate the font in FontBook on your Mac. Open the font in FontBook by double clicking the ttf file. Then, right click on the font and select "Validate Font". See if it passes the validation.

      • Anonymous October 21, 2010 at 1:33 pm #

        You're correct. I discovered the problem yesterday. I apologize for not updating on the comment system.

        Thank you for taking the time to reply. Fantastic article as always.

  6. Thomas Phinney November 4, 2010 at 5:35 pm #

    This is great, but it's worth keeping in mind that you can't use just any old "quality font"; fonts are software, and for relatively few do to the initial license terms allow you to just embed them in your apps. Even many free fonts are only "free" for non-commercial use.

    So, check the license terms, and if in doubt contact the font vendor….

    • Nathan Eror November 4, 2010 at 6:48 pm #

      Absolutely. Font licensing is a nasty business. Unless the font license specifically states that it can be embedded in a digital download, stay away. These days, most modern font sellers provide a separate (and much more expensive) license for embedding in digital downloads. Some of them only allow for a certain number of downloads, too.

  7. Nico Prananta November 13, 2010 at 6:44 am #

    Hello Nathan! Thanks for the tutorial, been looking for something like this. But I want to ask, how to calculate the CATextLayer's frame size of a given NSString using the custom font? Can you help me with this? I know we can use NSString's -sizeWithFont:, but it only accept UIFont. Thanks.

  8. aczs December 21, 2010 at 4:54 pm #

    never seem something more complicated like this.
    Add your .ttf file to the project, add UIAppFonts key to info.plist ("Font supported by the application")

    then create UIFont with:
    UIFont *myFont = [UIFont fontWithName@"myFont" size:17]

    and assign the font to the textView (e.g.)
    myUITextView.font = myFont;

    it's as easy as that and this is possible since iOS 3.2.

Trackbacks/Pingbacks

  1. iPad and iOS 4 Custom Font Loading » Free Time Studios Blog - September 20, 2010

    […] on the response I received to my last post about Custom Fonts on the iPad and iOS 4, iOS font handling and Core Text are a bit of a mystery to many iOS developers. Because of that, […]

Leave a Reply