Been digging into details about UIFont and Dynamic Type recently. Come swim with me in a sea of tiny details for a moment. 🧵
So generally speaking there's two canonical ways to get fonts that support Dynamic Type: preferred fonts and system fonts + UIFontMetrics.
The preferred font APIs are great if you just need default point sizes, but if you need different point sizes or weights, things get...weird
First, it's important to know that preferredFont APIs adjust font leadings, while systemFont APIs do not.
In other words, systemFont APIs result in denser lines of text than equivalent preferredFont APIs which have been tuned.
The preferred fonts APIs go out of their way to dial in this leading behavior, with different leading values being assigned to the font at every size category:
It's not just line spacing that's different, though. UITableView section headers will compensate for non-zero leading by reducing the amount of padding in section headers to afford *more* information density on screen.
So preferredFont is the way to go...but what happens when you need a font with a different point size or weight?
Create the system font you need and pass it through UIFontMetrics' scaledFontForFont:, right?
Not quite.
Remember that first detail about line spacing? Turns out even if you pass a systemFont through UIFontMetrics, that leading detail is never applied. 😱
Thankfully it is possible to both create a font with a custom point size *and* maintain Apple's dialed in leading values, but prior to iOS 14 it was a bit of a mixed bag of behavior.
The dance:
1. Use UIFontDescriptor.preferredFontDescriptorWithTextStyle to make a font descriptor. Add weight/italic attributes as needed. 2. Pass it to UIFont.fontWithDescriptor:size: 3. Associate a text style with the font via UIFontMetrics.
This only really works on iOS 14
For iOS 12 and 13 there's a scattering of iOS bugs and unexpected behavior that result in a windy road toward a swiss cheese of dialed in fonts, but it kind of works for common cases. You can see the different considerations in gist.github.com/jverkoey/e5965….
With the iOS 14 solution, your custom preferred fonts' leading will now dynamically adjust based on the initial point size 💯
Here's a screenshot of some of the leading values that will be generated:
Note that the trade-off to this approach is that the low and top-end size category point sizes are different than the equivalent direct preferredFont API, even for the same base point size, so you will want to consistently use the dance outlined above if you use it at all.
- Don't just use system font APIs. Prefer preferred font APIs.
- For custom sizes/weights, use preferred font descriptors to make Dynamic Type-compatible system fonts that also adjust leading.
- Consider using traitLooseLeading and traitTightLeading if needed.
fin.
Also: I'd love to make clarifications + improvements to this if others have encountered similar things. Please let me know if anything's totally off the mark!
• • •
Missing some Tweet in this thread? You can try to
force a refresh