Chapter 16
Working with Custom Fonts and Dynamic Type

When you add a label to a view, Xcode provides the option to modify the font type using the Attribute inspector. From there, you can select a system font or choose a custom font from the pre-defined font family.

But what if you can't find a suitable font from the default font family that aligns with your app's design? Typography plays a crucial role in app design, and utilizing the right typeface can greatly enhance your app's overall appeal. In such cases, you might want to consider using custom fonts created by third-party designers, which are not included in the standard Mac OS font library. A quick search on Google will yield numerous free fonts available for app development.

However, incorporating these custom fonts into your Xcode project can be a bit more challenging. Simply adding the font file to the project won't suffice. In this chapter, I will guide you through the process of bundling new fonts and provide step-by-step instructions.

As always, I will provide a demonstration and build the demo app together with you. The demo app is straightforward, consisting of a series of labels that showcase different custom fonts.

You can start by building the demo from scratch or downloading the template from http://www.appcoda.com/resources/swift59/CustomFontStarter.zip.

Download Custom Fonts

We'll use a few fonts that are available for free. However, we're not allowed to bundle and distribute them via our project template. Before proceeding, download the following fonts via the below links:

Alternatively, you can use any fonts that you own for the project. Or you are free to use some of the beautifully designed fonts from:

Adding Font Files to the Project

Just like any other resource file (e.g. image), you have to first add the font files to your Xcode project. I like to keep all the resource files in a font folder. In the project navigator, right-click the CustomFont folder and select New Group to create a folder. Change the name to font. Then drag the font files that you have downloaded into the folder.

Figure 16.1. Adding fonts file to the Xcode project
Figure 16.1. Adding fonts file to the Xcode project

When Xcode prompts you for confirmation, make sure to check the box of your targets (i.e. CustomFont) and enable the Copy items if needed option. This instructs Xcode to copy the font files to your app's folder. If you have this option unchecked, your Xcode project will only add a reference to the font files.

Figure 16.2. Choose options for adding the files
Figure 16.2. Choose options for adding the files

The font files are usually in .ttf or .otf format. Once you add all the files, you should find them in the project navigator under the font folder.

Figure 16.3. Previewing the fonts in Xcode
Figure 16.3. Previewing the fonts in Xcode

Register the Fonts in the Project Info Settings

Before using the font faces, you have to register them in the project settings. Select the Info plist, add a new property named Fonts provided by application. This is an array key that allows you to register the custom font files.

Right-click one of the keys and select Add Row from the context menu. Scroll and select Fonts provided by application from the drop down menu. Click the disclosure icon (i.e. triangle) to expand the key. You should see Item 0. Double click the value field and enter Hallo sans black.otf. Then click the + button next to Item 0 to add another font file. Repeat the same step until all the font files are registered - you'll end up with a screenshot like the one shown below. Make sure you key in the file names correctly. Otherwise, you won't be able to use the fonts.

Figure 16.4. Configuring the font files in Info.plist
Figure 16.4. Configuring the font files in Info.plist

Using Custom Fonts in Interface Builder

Once you embed the fonts files in your project, Xcode allows you to preview the fonts in Interface Builder. Any custom fonts added to your project will be made available in Interface Builder. You can change the font of an object (e.g. label) in the Attributes inspector and Interface Builder will render the result in real-time.

Figure 16.5. Using custom fonts in Interface Builder
Figure 16.5. Using custom fonts in Interface Builder

Using Custom Fonts in Code

Alternatively, you can use the custom font through code. Simply instantiate a UIFont object with your desired custom font and assign it to a UI object such as UILabel. Here is an example:

label1.font = UIFont(name: "Mohave-Italic", size: 25.0)
label2.font = UIFont(name: "Hallo sans", size: 30.0)
label3.font = UIFont(name: "Canter Light", size: 35.0)

If you insert the above code in the viewDidLoad method of the ViewController class and run the app, all the labels should change to the specified custom fonts accordingly.

For starters, you may have a question in your mind: how can you find out the font name? It seems that the font names differ from the file names.

That's a very good observation. When initializing a UIFont object, you should specify the font name instead of the filename of the font. To find the name of the font, you can right-click a font file in Finder and select Get Info from the context menu. The value displayed in the Full Name field is the font name used in UIFont. In the sample screenshot, the font name is Canter Light.

Figure 16.6. Finding out the font name
Figure 16.6. Finding out the font name

Working with Dynamic Type

Dynamic Type is not something new. It has been around since the release of iOS 7. With Dynamic Type, users are able to choose their own font size, as long as the app supports Dynamic Type.

Figure 16.7. Enable large font size
Figure 16.7. Enable large font size

So far, all the labels we worked with are of a fixed font size. Even if you go to Settings > General > Accessibility and enable Larger Text, you will not be able to enlarge the font size of the demo app.

Enabling Dynamic Type

To make your app work with Dynamic Type, you have to set the font of the labels to a text style (instead of a specific font). Similar to what we have done before, you can change the font style through Interface Builder or using code. If you go to Main storyboard, select one of the labels. You can change its font in the Attributes inspector. Instead of using a custom font, choose a text style (say, Title 1).

Figure 16.8. Changing the label to a text style
Figure 16.8. Changing the label to a text style

If you prefer to set the font programmatically, replace the code in the viewDidLoad() method of ViewController.swift with the following:

override func viewDidLoad() {
    super.viewDidLoad()

    label1.font = UIFont.preferredFont(forTextStyle: .title1)
    label2.font = UIFont.preferredFont(forTextStyle: .headline)
    label3.font = UIFont.preferredFont(forTextStyle: .subheadline)

}

The preferredFont method of UIFont accepts one of the following text style:

  • .largeTitle
  • .title1, .title2, .title3
  • .caption1, .caption2
  • .headline
  • .subheadline
  • .body
  • .callout
  • .footnote

Now open the simulator and go to Setting to enable larger text (see figure 16.7). Once configured, run the app to have a look. You will see the labels are enlarged.

Figure 16.9. Dynamic Type in action
Figure 16.9. Dynamic Type in action

Adjust the Font Automatically

If you go back to Settings to adjust the preferred font size and reopen the demo app, the size of the font is unchanged. Right now, the size of the font keeps intact once the app is launched.

How can you enable the app to adjust the font size whenever the user changes the preferred font size in Settings?

If you use Interface Builder, select the label and go to the Attributes inspector. Tick the checkbox of the Automatically Adjusts Font option. The label will now adjust its font size automatically.

Figure 16.10. Enable the Automatically Adjusts Font option
Figure 16.10. Enable the Automatically Adjusts Font option

Alternatively, you can set the adjustsFontForContentSizeCategory property of the label object to true so as to make sure the label responds to the text size changes.

label1.adjustsFontForContentSizeCategory = true
label2.adjustsFontForContentSizeCategory = true
label3.adjustsFontForContentSizeCategory = true

Using Custom Fonts for Text Styles

The default font of any text styles is set to San Francisco, which is the system font of iOS. What if you need to use a custom font? How can you use your own font and take advantage of Dynamic Type at the same time?

In iOS 11 (or up), developers can scale any custom font to work with Dynamic Type by using a new class called UIFontMetrics. Before I explain what UIFontMetrics is and how you use it, let's think about what you need to define when using a custom font (say, Mohave) for Dynamic Type.

Apparently, you have to specify the font size for each of the text styles. Say, the .body text style should have the font size of 15 points, the .headline text style is 18 points, etc.

Remember this is just for the default content size. You will have to provide the font size of these text styles for each of the content size categories. Do you know how many content size categories iOS provides?

Go back to figure 16.7 and do the counting.

If you count it correctly, there are a total of 12 content size categories. Combining with 11 text styles, you will need to set a total of 132 different font sizes (12 content size categories x 11 text styles) in order to support Dynamic Type. That's tedious!

This is where UIFontMetrics comes into play to save you time from defining all these font metrics. Instead of specifying the font metrics by yourself, this new class lets you retrieve the font metrics of a specific text style. You can then reuse those metrics to scale the custom font. Here is a sample usage of scaling a custom font for the text style .title1:

if let customFont = UIFont(name: "Mohave-Italic", size: 28.0) {
    let fontMetrics = UIFontMetrics(forTextStyle: .title1)
    label.font = fontMetrics.scaledFont(for: customFont)
}

You can now modify the viewDidLoad() method to the following code snippet:

override func viewDidLoad() {
    super.viewDidLoad()

    if let customFont1 = UIFont(name: "Mohave-Italic", size: 28.0) {
        let fontMetrics = UIFontMetrics(forTextStyle: .title1)
        label1.font = fontMetrics.scaledFont(for: customFont1)
    }

    if let customFont2 = UIFont(name: "Hallo sans", size: 20.0) {
        let fontMetrics = UIFontMetrics(forTextStyle: .headline)
        label2.font = fontMetrics.scaledFont(for: customFont2)
    }

    if let customFont3 = UIFont(name: "Canter-Light", size: 17.0) {
        let fontMetrics = UIFontMetrics(forTextStyle: .subheadline)
        label3.font = fontMetrics.scaledFont(for: customFont3)
    }

    label1.adjustsFontForContentSizeCategory = true
    label2.adjustsFontForContentSizeCategory = true
    label3.adjustsFontForContentSizeCategory = true

}

This is how you use custom fonts and make it work with Dynamic Type. Run the app to have a quick test. Also, remember to adjust the preferred font size in Settings to see the text size changes.

Figure 16.11. Using custom fonts with Dynamic Type
Figure 16.11. Using custom fonts with Dynamic Type

In the code above, we initialized the custom font object with a default font size. It is up to you to decide the font size at the default content size. However, you can always refer to Apple's default typeface for reference.

For the San Francisco typeface, Apple published its font metric used for different content size categories in the iOS Human Interface Guidelines. Figure 16.12 shows the font size of all the text styles at the Large content size.

Figure 16.12. The font metrics Apple defines for its San Francisco typeface at the Large content size
Figure 16.12. The font metrics Apple defines for its San Francisco typeface at the Large content size

Summary

Apple has invested significant effort in promoting the use of Dynamic Types among developers. The introduction of UIFontMetrics has made it simpler to scale custom fonts and ensure compatibility with Dynamic Type. When developing your apps, it's crucial to consider the diverse user base they will reach. Some users may prefer smaller text sizes, while others may require larger sizes for comfortable reading. If your apps haven't yet incorporated Dynamic Type, it's high time to prioritize adding it to your To-Do list.

To continue reading and access the full version of the book, please get the full copy here. You will also be able to access the full source code of the project.

results matching ""

    No results matching ""