fbpx

Blogs from the Ranch

< Back to Our Blog

Make iOS 7’s Dynamic Type Easier to Use with our Open-Source BNRDynamicTypeManager

Avatar

John Gallagher

With iOS 7, Apple introduced a system called Dynamic Type that allows applications to use fonts that vary in size based on the device’s configuration. Taking advantage of Dynamic Type adds polish to an app, but unfortunately, it can feel cumbersome to use. BNRDynamicTypeManager is an open-source library that makes it less painful.

Typical Dynamic Type Support

At first glance, using Dynamic Type seems very simple: choose one of the new text styles as your font in Interface Builder, or use +[UIFont preferredFontForTextStyle:] in code, and then when you run your app, you get a dynamically sized font!

Everything looks great… until you switch to the device preferences and change the font size while your app is running. Switching back to your app, you’ll find that it still shows the old size. And if you dig into the documentation, and you’ll find that Apple mentions that it’s “important to observe the UIContentSizeCategoryDidChangeNotification so that you can re–lay out the text when the user changes the content size category.”

In practice, this means you probably end up modifying the view controller(s) that contain labels or buttons or other elements that use the new text styles, have them add themselves as observers for UIContentSizeCategoryDidChangeNotification, and you have a little helper method that reassigns the fonts of all the labels, such as:

    - (void)didReceiveUIContentSizeCategoryDidChangeNotification:(NSNotification *)note
    {
        self.bodyLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
        self.headlineLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
        self.fooButton.titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
        // etc.
    }

After writing that a few times, and especially if writing that involved making IBOutlets for labels that you otherwise don’t need in your view controller, you probably start thinking that there has to be a better way.

Well, we made one: the BNRDynamicTypeManager library. Here’s how to use it.

Using the BNRDynamicTypeManager library

Installing BNRDynamicTypeManager

If you’re already using Cocoapods, add BNRDynamicTypeManager to your Podfile:

    pod 'BNRDynamicTypeManager', '~> 0.1.0'

If you’re not, grab the relevant files out of the GitHub repository and add them to your project.

Using BNRDynamicTypeManager with Interface Builder

In the simplest case, if you just want to have labels, buttons, text fields or text views that have a text style-based font, change the class of the element in the XIB to the appropriate BNRDynamicTypeManager subclass (e.g., UILabel becomes BNRDynamicTypeManagedLabel – see the README for the full list).

That’s it! Now that element will use the appropriate font on startup, and its font will be updated for you whenever the app receives UIContentSizeCategoryDidChangeNotification.

Using BNRDynamicTypeManager Programatically

If you create UI elements programatically, or can’t change your XIB classes for some other reason (perhaps you’ve already subclassed UILabel for your own purposes), you can explicitly tell BNRDynamicTypeManager to watch elements for you. For the common UIKit classes (UILabel, UIButton, UITextField, UITextView), there are methods provided:

    [[BNRDynamicTypeManager sharedInstance] watchLabel:label
                                             textStyle:UIFontTextStyleBody];
    [[BNRDynamicTypeManager sharedInstance] watchButton:button
                                              textStyle:UIFontTextStyleBody];
    // etc.

If you want to watch an element that isn’t a subclass of the supported classes but has a UIFont *font reachable via a keypath, you can use -[BNRDynamicTypeManager watchElement:fontKeypath:textStyle:]. There is no associated “unwatch” method. BNRDynamicTypeManager doesn’t keep strong references to the views it’s watching, so it doesn’t interfere with their normal lifecycles.

Under the Hood

Internally, BNRDynamicTypeManager is mostly a thin wrapper around NSMapTable that adds itself as an observer for UIContentSizeDidChangeNotification. NSMapTable is what allows us to hold weak references to the elements we’re watching while still keeping track of the text style to use (and the keypath to the object’s font). The BNRDynamicTypeManager class itself isn’t very big; if you’re curious, I’d recommend having a look.

Avatar

John Gallagher

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project