Advanced Git, Second Edition

Git is key to great version control and collaboration on software projects.
Stop struggling with Git and spend more time on the stuff that matters!

Home iOS & Swift Tutorials

Custom Fonts: Getting Started

Learn how to use custom fonts on iOS with Storyboards, UIKit and SwiftUI.

5/5 1 Rating

Version

  • Swift 5.5, iOS 15, Xcode 13

So you’ve built a cool app. Nice one! However, it’s feeling a bit bland and boring. Your app needs a personality, and you want to make it look like a million dollars. Unfortunately, you don’t actually have a million dollars to spend on a branding expert. Fortunately, one way to instantly improve your app and make it look a million dollars is by using a custom font.

Not only does iOS fully support using custom fonts in your apps, but there are thousands of free fonts readily available online to use commercially.

In this tutorial, you’ll get a feel for using custom fonts within your own apps by improving a simple reading list app. Along the way, you’ll learn:

  • A brief history of fonts and why you might choose one over another.
  • How to install a custom font in your project.
  • How to use a custom font with SwiftUI, Storyboards and programmatically with UIKit.
  • What Dynamic Type is, and why it’s used.
  • How to support Dynamic Type in your app.

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial. Then, open ReadingList.xcodeproj from the starter folder and build and run the app.

You’ll see a list of titles of three excellent beginner books from raywenderlich.com:

ReadingList app starter project

The font used here is nothing special; it’s the system font provided by iOS. With the help of some custom fonts, you’ll jazz this app up a bit!

Going Back to the Future

You might have heard the words font and typeface thrown around, but what exactly is the difference? Strictly speaking, a typeface is a set of letters that share a common design — for example, the typeface Arial. Meanwhile, a font is a particular set of those letters within a typeface — for example, size 10px Arial or size 16px Arial Bold. These are different fonts, but they share the same typeface. Makes sense, right? Nowadays, font and typeface are often used interchangeably, so you’ll often see one used when the other is more strictly correct.

Before choosing fonts to use in your app, it’s good to understand how technology has progressed to the fonts in use today. In fact, Steve Jobs said that the only reason the Mac supported multiple typefaces was because he had studied the history of typefaces and realized the importance of them.

So, going back in time…

Before the mid-15th century, everyone used to write by hand. Then, a guy named Johannes Gutenberg realized that this was extremely slow and expensive, so he created the printing press. This had reusable and moveable letter blocks. The font used was in the style of the Blackletter calligraphy typeface used in manuscripts, which looked a bit like this:

Blackletter font style

The font letters took up a considerable amount of room on the page, and through the centuries, this rather cumbersome font was eventually ditched for more efficient and smaller fonts. Different styles flourished and, fast-forwarding into the 20th century, they moved from the printing press into the digital era.

In the late 1980s, Apple developed the TrueType font standard, which allowed computer displays and printers to use one font file and provided a wide range of options to the font developer. Then, Microsoft developed the OpenType font standard, which superseded TrueType to allow cross-platform font files and more advanced typographic behavior.

In 2016, OpenType adopted support for variable fonts, which allow for a variety of widths, thickness, shape and slant, all in one font file. Despite this, most platforms now support both TrueType and OpenType fonts, so you’ll still see TrueType fonts used a lot.

On iOS, Apple provides a typeface with varying styles called San Francisco (SF). SF Pro is the default system font that any UI element will automatically use when you add it to your interface.

Choosing a Font

SF Pro falls under the category of a sans-serif type. This is one of five basic classifications of typefaces:

  • Serif: These typefaces have a slight projection, called a serif, that finishes off a stroke of a letter. They’re typically used in title text to give a classic or more elegant look. An example of these typefaces is Garamond. The text you’re currently reading is also rendered in a serif font.
  • Sans-serif: These are typefaces that, as you might have guessed (if you know French!), don’t have serifs. These are widely used in body text due to having a cleaner, more legible appearance than serif types. An example of these typefaces is Arial.
  • Script or Handwriting: These look like handwriting and contain flourishes and loops. They’re useful in both formal and casual settings. An example of these typefaces is Zapfino.
  • Monospaced: All the letters in these typefaces occupy an equal amount of horizontal space. These were common in typewriters and computers in the past. They’re rarely used in body text, but they’re usually used in scripts for the television or stage. An example of these typefaces is Courier New. You’ll also typically use one of these in your terminal application.
  • Display: These vary in appearance, and they’re often eccentric and less restrained than the other categories. Their main purpose is for large headings and titles rather than body text. An example of these typefaces is Monoton.

Different font styles showing the name of the font and the name of the font style written in the font itself.

As you can see, when it comes to typefaces, there many different types (no pun intended). A simple internet search will show you many font directories where you can find free fonts to download. However, you have to be careful, as they’re not always commercially available or don’t come with the right license. A reliable source for fonts is Google Fonts. A good rule of thumb for choosing a high-quality font is to rely on typefaces that have a lot of different weights.

Take a quick look at some fonts. Head over to https://fonts.google.com. Then choose Sans Serif as the Category. Filter by Number of styles and set it to 10+:

Google Fonts selection

Take a look at the available fonts and familiarize yourself with Google Fonts.

Installing a Font

In a production app, you’ll want to choose one — at a stretch, two — fonts to convey your brand’s feel and personality. However, for this tutorial, there are three pre-selected fonts included in your materials starter folder. These fonts are probably not suitable for body text in a production app, but they’re good for demonstration purposes.

Drag Prata-Regular.ttf from the tutorial resources’ starter folder into the Fonts folder in Xcode. Select Copy items if needed and also select the ReadingList target in the Add to targets list:

Adding a font to an Xcode project

Now, open Info.plist and add a key by hovering over any row and clicking the + button. Then choose Fonts provided by application for the new key. Whenever you add a custom font, you need to insert an element into the array for this key. So, set the value of Item 0 in the array to Prata-Regular.ttf:

Custom font added to info.plist

Your first font is now installed and ready to use within the app. Repeat the steps above for the other two fonts included in your starter folder: StyleScript-Regular.ttf and Texturina.ttf.

You now have all three fonts included in the project!

Build and run. Although there are no build errors, there’s also no visual change. So now it’s time to use the installed font!

Using Custom Fonts in Storyboards

Open Main.storyboard and click the Swift Apprentice label in the BookListViewController. In the Attributes inspector, click the font icon next to the font name. Click the Font drop-down and change it from System to Custom. Click the Family drop-down and choose Texturina. Keep the Style drop-down as Regular and set the Size to 16:

Adding a custom font to storyboard

You’ll see the font of your label has already changed in the storyboard! Build and run your app to confirm this:

First label font changed

In the image above, you can see the change to the font of the “Swift Apprentice” label. Neat!

That’s great, but it’s only Storyboards. There are more ways to use text in iOS!

Using Custom Fonts in UIKit Programmatically

So you now know how to use a custom font in storyboards. But what if you want to use a custom font in a UI element created using UIKit?

The second book title label in the list is created programmatically outside of the Storyboard. You’ll use another font for this label.

First, you need to find the name of the font. It’s not necessarily the same as the filename of the font. After all, you could change the filename! So you’ll add some code that lists the names of fonts available to the app.

Open BookListViewController.swift and add the following code inside viewDidLoad():

for family in UIFont.familyNames.sorted() {
  let names = UIFont.fontNames(forFamilyName: family)
  print("Family: \(family) Font names: \(names)")
}

Build and run. The code above prints out to the console the font family and name of all the fonts available:

A list of fonts available in the app

Search for Style Script in the console. You’ll see that its name is StyleScript-Regular. This is a key that you can use to refer to the font when you want to use it in your app.

This time, the name of the font happens to be the same as the filename, but next time, you might not be so lucky! Now that you’ve got the name, delete the code you just added.

Now, it’s time to apply the font to your second label.

But wait! What if you have multiple UI elements that require custom fonts? If a designer comes along and decides that the font you used isn’t right, you’re going to have to spend ages trawling through the app finding all the places where you used the font. What a nightmare! For this reason, it’s always best practice to have a single source of truth.

In this case, you’ll extend UIFont and create a single source of truth for the font used within your app.

In the Extensions folder, open UIFont+CustomFont.swift, and place the following static method inside the extension:

static func scriptFont(size: CGFloat) -> UIFont {
  guard let customFont = UIFont(name: "StyleScript-Regular", size: size) else {
    return UIFont.systemFont(ofSize: size)
  }
  return customFont
}

This returns an instance of your custom font at a given size. However, if the font file fails to load, it returns the system font as a fallback. Notice that you’re using the name of the font you found earlier, namely StyleScript-Regular.

To use this with your second label, open BookListViewController.swift. In addSecondBookLabel, add the following above view.addSubview(label):

label.font = UIFont.scriptFont(size: 16)

This uses the method you just added to obtain the script font with a size of 16.

Build and run.

Two labels using custom fonts

You can see that the second label has now changed to use the script font.

Using Custom Fonts in SwiftUI

For your final label, which is a SwiftUI element, you’ll use a different font, which has the name Prata-Regular. As before, you’ll need a single source of truth for your SwiftUI fonts. For this, you’ll extend Font.

In the Extensions folder, open Font+CustomFont.swift and add the following inside the extension:

static func prataFont(size: CGFloat) -> Font {
  return .custom("Prata-Regular", size: size)
}

This returns a custom font of your specified size to use within SwiftUI elements.

Open BookTitleView.swift and replace .font(.system(size: 16)) with:

.font(.prataFont(size: 16))

Build and run.

All labels using custom fonts

You can see all three labels are now using custom fonts!

Now that you’ve learned how to use custom fonts in three different scenarios, it’s time to take things a little further and learn how you can get your fonts to scale automatically for users who request that.

Scaling Font Size Automatically With Dynamic Type

Since iOS 7, text-based UI elements have supported a feature called Dynamic Type. This allows users to specify their preferred text size on screen via Accessibility in the Settings app. This allows for greater readability, and supporting this within your app provides a more consistent reading experience across apps.

To support this in your app you make use of Text Styles. These tell the system how to scale your text relative to a specific style. There are many different text styles for different sizes.

Since all your labels represent book titles, you’ll go with the title1 style.

Updating for Dynamic Type in Storyboards

To update your storyboard label for Dynamic Type, open Main.storyboard, click the Swift Apprentice label and check the Automatically Adjusts Font checkbox in the Attributes inspector:

Supporting Dynamic Type in Storyboards

This tells the label that it should automatically adjust the font size when the user’s preferred text size changes. To do this, the label needs to use a scaled instance of a font. For system fonts, you do this by assigning the label a specific text style in the Attributes inspector. However, for custom fonts, you need to provide the scaled instance of the font via code.

Note: Xcode will raise a warning this option requires using a Dynamic Type text style. You can ignore this warning as you’ll provide the scaling in code.

So, open UIFont+CustomFont.swift and add the following inside the extension:

func dynamicallyTyped(withStyle style: UIFont.TextStyle) -> UIFont {
  let metrics = UIFontMetrics(forTextStyle: style)
  return metrics.scaledFont(for: self)
}

This creates a UIFontMetrics object that specifies the text style and then passes your custom font to scaledFont(for:) to create a scaled font based on the text style.

Open BookListViewController.swift and add the following to the end of the class:

private func dynamicallyTypeFirstBookLabel() {
  firstBookLabel.font = firstBookLabel.font.dynamicallyTyped(withStyle: .title1)
}

In viewDidLoad(), add the following after addSecondBookLabel():

dynamicallyTypeFirstBookLabel()

Build and run. You’ll see something like this:

All labels using custom fonts

Unless you’re already using accessibility features on your device, you’ll notice there’s no visual change.

Open your Settings app, tap Accessibility, then tap Display & Text Size, and tap Larger Text. To see an obvious change in text size within your app, flip the Larger Accessibility Sizes switch to the on position. Then drag the text size slider at the bottom to the maximum value:

Increasing Dynamic Type text size

Head back to your app to take a look.

Increased text size in two labels

You’ll see the text in both the first and last label has increased in size. That’s the Storyboard label and the SwiftUI label changing size automatically. The code you just added is what controls the Storyboard label. But why did the SwiftUI label also change?

Updating for Dynamic Type in SwiftUI

As you saw, the SwiftUI label text size increased despite not having specified it. That’s because it scales with the body text style by default. However, since you want a title style, rather than a body style, you’ll need to change that.

In Font+CustomFont.swift, replace prataFont(size:) with:

static func prataFont(
  withStyle style: Font.TextStyle,
  size: CGFloat
) -> Font {
  return .custom("Prata-Regular", size: size, relativeTo: style)
}

This now returns your custom font relative to a specified text style.

Update your SwiftUI label by going to BookTitleView.swift and replacing .font(.prataFont(size: 16)) with:

.font(.prataFont(withStyle: .title, size: 16))

Build and run.

Two dynamically typed labels that are correctly scaled

You’ll see that your third label scales correctly for its text style now.

But what about the middle label? That is the one created with UIKit programmatically. It’s time to support Dynamic Type in that one too!

Updating for Dynamic Type in UIKit Programmatically

To support Dynamic Type in a programmatically created label, you’ll need to make some changes to your UIFont extension. Open UIFont+CustomFont.swift and replace scriptFont(size:) with the following:

static func scriptFont(
  withStyle style: UIFont.TextStyle,
  size fontSize: CGFloat
) -> UIFont {
  guard let customFont = UIFont(
    name: "StyleScript-Regular",
    size: fontSize)
  else {
    let descriptor = UIFontDescriptor
      .preferredFontDescriptor(withTextStyle: style)
    return UIFont(descriptor: descriptor, size: descriptor.pointSize)
  }
  return customFont.dynamicallyTyped(withStyle: style)
}

This method’s job is to create a UIFont object for the StyleScript-Regular font at a given size and with a given style. First, you find the font and then, you use the previously created method, dynamicallyTyped(withStyle:), to scale your custom font. However, if the custom font initialization fails for any reason, a fallback UIFontDescriptor provides a system font that matches the specified text style.

Now open BookListViewController.swift, and in addSecondBookLabel(), replace label.font = UIFont.scriptFont(size: 16) with:

label.adjustsFontForContentSizeCategory = true
label.font = UIFont.scriptFont(withStyle: .title1, size: 16)

This tells the label to adjust the font size automatically when the user’s preferred text size changes. Then, it assigns your scaled font to the label.

Build and run. Now all the labels support Dynamic Type!

All three labels using dynamic type

Feel free to play around with the font size from the Settings app and see how the font scales as you do.

Extending Dynamic Type

Sometimes, you might want to adjust the layout and sizing of non-text elements on the screen based on the user’s preferred font size. You’ll employ that technique here to adjust the size of the book image at the top of the screen.

Open BookListViewController.swift and add the following below addSwiftUILabel(_:):

override func traitCollectionDidChange(
  _ previousTraitCollection: UITraitCollection?
) {
  // 1
  super.traitCollectionDidChange(previousTraitCollection)

  // 2
  let preferredContentSizeCategory = traitCollection
    .preferredContentSizeCategory

  // 3
  switch preferredContentSizeCategory {
  case .accessibilityLarge,
    .accessibilityExtraLarge,
    .accessibilityExtraExtraLarge:
    bookHeightConstraint.constant = 200
  case .accessibilityExtraExtraExtraLarge:
    bookHeightConstraint.constant = 300
  default:
    bookHeightConstraint.constant = 100
  }

  // 4
  view.layoutIfNeeded()
}

This method is automatically called every time the user updates text size preferences. You handle these changes by:

  1. Calling the superclass’s implementation so that UIKit can do anything it needs to itself.
  2. Getting the preferredContentSizeCategory, which is the font sizing option preferred by the user.
  3. Adjust the height constraint attached to the book image view depending on the value of preferredContentSizeCategory.
  4. Telling the view to update its layout.

This method only gets called when the preferred font size changes. If the user sets their preferred text size before opening the app, it won’t call the method, and the image view won’t be the correct size. For this reason, you’ll need to call it when the view loads.

Add the following to the bottom of viewDidLoad():

traitCollectionDidChange(nil)

Then, build and run.

With your preferred font size set to the largest setting, you’ll see the book image view has now increased in size:

Image view size reflecting user's text size preference

Play around with different preferred text sizes to confirm your code works as expected.

You’ve now successfully employed Dynamic Type to adjust UI elements other than text. And that’s also the end of the tutorial where you’ve learned all about custom fonts!

Where to Go From Here?

Download the completed project files by clicking the Download Materials button at the top or bottom of the tutorial.

In this tutorial, you learned:

  • The history of fonts.
  • Installing and use custom fonts in your project.
  • Using custom fonts in Storyboards, SwiftUI elements and programmatically created UIKit elements.
  • Supporting Dynamic Type, including using it to scale non-text UI elements.

You can now use this knowledge to create a more polished-looking app and wow your users!

If you’d like to learn more about supporting Dynamic Type within your app, check out our Dynamic Type video course. Or, read our Auto Layout by Tutorials book for more information about managing Dynamic Type in your layout.

We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!

Average Rating

5/5

Add a rating for this content

1 rating

More like this

Contributors

Comments