Fonts in UIKit

Introduction

Today was such an enlightening day in iOS development, I just have to write it all out to retain this information somewhere. I can’t write it all out in one post, since it’s just too much information ^_^;.

The First Topic is regarding fonts in UIKit, and how versatile they are, and very easy to modify with the help of TextKit.

Fonts Master

My first task for the day was to figure out the best way to equally space out the numbers in our app’s font. To understand what I mean, here is a gif:

image

Ideally, the counter doesn’t jump all over the place when the numbers change. In order for that to happen, the number spacing must all be equal.

I had two other problems that I was postponing to a later time.

The first one was using Western Arabic numerals with an Arabic font that uses Eastern Arabic numerals.

The second problem, which I never really solved, is defining font fallbacks. I mean, if the app is displaying Arabic text with some english letters, I need to set the font to the Arabic custom font, but have a fallback font for the English text.

Naive Approach

I solved the first two problems in the past using a naive, un-reusable approach: Altering the font file.

Using FontForge, I went over all the numbers, and made sure they all had equal spacing. Then, I copied the Arabic font, and swapped the Eastern numerals with western ones. It worked out great, however…

The process of editing and rebuilding a font is tedious. Even worse, I have to do it again now, since we are using a different font for the app update. To top it all off, I never solved the fallback issue, since it required me to somehow merge the English font with the Arabic font, and I never found the time to do it.

Font Descriptors

Introduced in iOS 7 alongside TextKit was the awesome UIFontDescriptor.

This font descriptor can be used later to create or modify a UIFont object. –– Apple Docs

WHOA! Create UIFont?! From the get go you get a feeling this thing is powerful! I will explain its power in context of how I’ll be using it, though.

MONOSPACE

OK, so this font descriptor thingie allows us to create fonts on the fly. Does that mean we need to create a copy of the original font, go over the numbers, and somehow draw them monospaced? … NOPE!

All we got to do is get a copy of the original font descriptor from the font we want to modify, and add a few attributes, and creating a new font by passing the new descriptor!

let newAttributes = [
    UIFontDescriptorFeatureSettingsAttribute: [
        UIFontFeatureTypeIdentifierKey: kNumberSpacingType,
        UIFontFeatureSelectorIdentifierKey: kProportionalNumbersSelector
    ]
]

That’s pretty much it! Full code snippet here.

NUMERALS

OK, so it was cool how we can play around with with font attributes, but how about completely swapping in and out characters? Simple enough!!

let cset: NSMutableCharacterSet = originalDescriptor.objectForKey(
    UIFontDescriptorCharacterSetAttribute
).mutableCopy()

cset.removeCharactersInString("1234567890")

/* NOTE: 
 * For those of you who are against hard coded string, you can
 * easily get a numeric character set, then subtract it from the
 * original character set using `NSMutableSet` properties that
 * `NSCharacterSet` provides.
 */

What happened? We simply copied the original characterset and removed the numerals. Wait so how is that gonna help us! Read on, my friend. By defining a proper fallback, numerals will no longer be used from this font :D

Font Fallbacks

If the previous epicness wasn’t enough, this will surely drive the point home. Font fallbacks, as described, is displaying multilingual text using multiple fonts, without the need to manually scan the text and assign different fonts yourself.

Apple calls it “Font cascading”. Here is a visual example from Apple’s talk:

image

Very simply, they added an arabic font descriptor to the cascaded list, and now the English “apple” font is the same, but the Arabic “apple” is using the cascaded font.

For those who like code, the dictionary is as follows:

[fontDescriptorByAddingAttributes: [
    UIFontDescriptorCascadeListAttribute: [arabicFontDescriptor]]
] 

Of course, we are only adding an Arabic font descriptor, but if you’re app is “very internationalized”, you can add n font descriptors, and take care of the order!

Conclusion

Tweaking your fonts at this granular level is what really defines the top notch quality of your app, and pleases those perfectionist customers.

PS: There is a very detailed talk about what I covered here in a 2013 WWDC talk.