How to combine Dynamic Type and monospaced font
This is a small tip for cases with countdown labels and similar UI.
Published: Dec. 16, 2022 Sponsored App StoreHave you ever had a label in your app that showed a live countdown? And have you noticed it to subtly shift horizontally when the countdown value changed?
This is because, by default, numbers have different widths. 1
takes less horizontal space than 5
, for example. And this can easily affect the total width of the label.
The basic solution is quite simple, thanks to UIFont.monospacedDigitSystemFont
method. You give it a size and weight, and it will return system font with all digits being the same width. And the horizontal shift is gone.
What about Dynamic Type?
I don’t need to repeat that having Dynamic Type support in your app is important. However, this gets tricky with monospaced fonts because there is no built-in method to give you monospaced font for the text styles.
Fortunately, we can fix this with the UIFontMetricts
and UIFontDescriptor
types and wrap it all in a neat little extension on UIFont
.
The result looks like this:
import UIKit
extension UIFont {
static func preferredMonospacedFont(for style: TextStyle, weight: Weight) -> UIFont {
let metrics = UIFontMetrics(forTextStyle: style)
let desc = UIFontDescriptor.preferredFontDescriptor(withTextStyle: style)
let font = UIFont.monospacedDigitSystemFont(ofSize: desc.pointSize, weight: weight)
return metrics.scaledFont(for: font)
}
}
And we are done!
Usage then looks something like this:
let countdownLabel = UILabel()
countdownLabel.font = .preferredMonospacedFont(for: .subheadline, weight: .bold)