Swift Regex Deep Dive
iOS MacOur introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
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.
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 IBOutlet
s 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.
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.
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
.
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.
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.
Our introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
The Combine framework in Swift is a powerful declarative API for the asynchronous processing of values over time. It takes full advantage of Swift...
SwiftUI has changed a great many things about how developers create applications for iOS, and not just in the way we lay out our...