Default UIFont size and weight but also support preferredFontForTextStyle

3.7k Views Asked by At

If I have my own group of UIFonts with different sizes and weights, for example:

let customFont03 = UIFont.systemFont(ofSize: 40, weight: .thin)

How can I support Dynamic Type while still preserving my custom size and weight as a default standard and scaling that depending on how the user selects accessibility sizes?

I'm not sure that preferredFont(forTextStyle:) is what I want because it only accepts a UIFont.TextStyle and I don't want to lock customFont03 in as a .body or .headline etc...

2

There are 2 best solutions below

0
On

UIFontMetrics is the class designed to solve this problem. In particular -[UIFontMetrics scaledFontForFont:] will do what you ask, but be sure to check out the other members of that class, which can do things like cap the font increase at some maximum (at the very largest content size categories a size 40 font would be scaled to a very large size indeed).

https://developer.apple.com/documentation/uikit/uifontmetrics/2877385-scaledfontforfont

0
On

Dynamic system font, specifying Style, Weight, and Italics

In Swift 5.

I can't believe Apple didn't provide a cleaner way to get a dynamic font with a specific weight. Here's my comprehensive solution, hope this helps!

extension UIFont {
    
    static func preferredFont(for style: TextStyle, weight: Weight, italic: Bool = false) -> UIFont {

        // Get the style's default pointSize
        let traits = UITraitCollection(preferredContentSizeCategory: .large)
        let desc = UIFontDescriptor.preferredFontDescriptor(withTextStyle: style, compatibleWith: traits)

        // Get the font at the default size and preferred weight
        var font = UIFont.systemFont(ofSize: desc.pointSize, weight: weight)
        if italic == true {
            font = font.with([.traitItalic])
        }

        // Setup the font to be auto-scalable
        let metrics = UIFontMetrics(forTextStyle: style)
        return metrics.scaledFont(for: font)
    }
    
    private func with(_ traits: UIFontDescriptor.SymbolicTraits...) -> UIFont {
        guard let descriptor = fontDescriptor.withSymbolicTraits(UIFontDescriptor.SymbolicTraits(traits).union(fontDescriptor.symbolicTraits)) else {
            return self
        }
        return UIFont(descriptor: descriptor, size: 0)
    }
}

You can use it like this:

 UIFont.preferredFont(for: .largeTitle, weight: .regular)
 UIFont.preferredFont(for: .headline, weight: .semibold, italic: true)