Intro to UIFontPickerViewController

A number of new font-management APIs were announced at WWDC 2019, including a standard system font picker for iOS and Catalyst apps (finally!). The session Font Management and Text Scaling covers the new APIs in detail; however, the sample code only exists on the slides and the documentation is currently very sparse. I thought it might be helpful to write out the sample code in a blog post, along with a little explanation.

UIFontPickerViewController is a customizable system font picker that, by default, has the following configuration:

  • displays all system fonts (including any fonts you include in your app bundle and that are specific to your app only)
  • shows only font families and not individual faces
  • uses a WYSIWYG presentation

To change these defaults, you can create a configuration object, initialize the font picker, and present it like so:

let config = UIFontPickerViewController.Configuration()
config.includeFaces = true
let fontPickerViewController = UIFontPickerViewController(configuration: config)
fontPickerViewController.delegate = self
self.present(fontPickerViewController, animated: true)

To display all font names using the system font, set displayUsingSystemFont to true.

You can also filter the font list by supplying an array of symbolic traits. For example, to display only monospaced fonts, you would do something like this:

let traits = UIFontDescriptor.SymbolicTraits(arrayLiteral: [.traitMonoSpace])
config.filteredTraits = traits

There are two delegate methods that allow you to control what happens after a user chooses a font or cancels font selection.

extension MyCustomViewController: UIFontPickerViewControllerDelegate {
    func fontPickerViewControllerDidPickFont(_ viewController: UIFontPickerViewController) {
        guard let fontDescriptor = viewController.selectedFontDescriptor else { return }
        let font = UIFont(descriptor: fontDescriptor, size: 48.0)
        textView.font = font
    }

    func fontPickerViewControllerDidCancel(_ viewController: UIFontPickerViewController) {
        print("User selected cancel.")
    }
}

Finally, if you want to display user-installed fonts, you need to add an entitlement to your app. In Xcode 11, go to your project settings, tap “Signing and Capabilities,” and add a new capability called “Fonts.” Under the Fonts entitlement are two options next to “Privileges.” Check the box by “Use Installed Fonts” to indicate that you want your app to read fonts that were installed by other apps.

iOS 13 Summer Plans

Yesterday I finally had some time to sit and think about what improvements I want to make to Snapthread this summer. I still want to rewrite the app using SwiftUI; however, after a bit of exploration, I think I may need to wait until it’s a little more capable. Here’s what I’m planning to do instead.

Phase 1

I want to leave the app in a good place for my iOS 11 and 12 users. To do that, I want to add a few more soundtracks to choose from and a tool for adjusting a video clip’s speed.

Phase 2

Based on everything that was revealed at WWDC, here’s what I want to do after I set the minimum OS target to iOS 13:

  • Rewrite my UICollectionView code to use the new compositional layout class and diffable data source
  • Redesign my photo library picker. Apple has deprecated the methods I was using to fetch “Moments,” so I will need to do something else to help users find the photos and videos they’re looking for.
  • Explore some of the new property wrappers, like @UserDefault
  • Replace my icons with SF Symbols and design a few custom ones
  • Replace my colors and font sizes with semantic ones and set the whole app to use dark mode
  • Use the new system provided font picker
  • Possibly rewrite two view controllers in SwiftUI: Settings and Soundtracks
  • If I have time, create some more custom Core Image filters

Doing everything on that list should help rid my code of most of its remaining bugs and set the app up well for the future. I can’t wait to get started!