How to scale system font in SwiftUI to support Dynamic Type?

In UIKit, I can change a UILabel's font size like this to support dynamic type using the system font:

UIFontMetrics.default.scaledFont(for: UIFont.systemFont(ofSize: 16))

Am I mistaken or is there no way to do such thing in SwiftUI? Can I only use the default font styles .title, etc. if I want to use the system font but also support Dynamic Type?

This solution from HackingWithSwift seems to only work with custom fonts, right?

Thanks in advance for any suggestions!


Solution 1:

The following approach works (tested with Xcode 11.2.1 / iOS 13.2.2)

var body: some View {
    Text("Hello, World!") // direct text .font
        .font(Font.system(size: UIFontMetrics.default.scaledValue(for: 16)))
}

as well as for view-based modifier

VStack {
    Text("Hello, World!")
}
.font(Font.system(size: UIFontMetrics.default.scaledValue(for: 16)))

Solution 2:

You can create a ViewModifier and a View extension just like in the HackingWithSwift article you mentioned, but that uses the system font instead.

struct ScaledFont: ViewModifier {
    // From the article:
    // Asks the system to provide the current size category from the
    // environment, which determines what level Dynamic Type is set to.
    // The trick is that we don’t actually use it – we don’t care what the
    // Dynamic Type setting is, but by asking the system to update us when
    // it changes our UIFontMetrics code will be run at the same time,
    // causing our font to scale correctly.
    @Environment(\.sizeCategory) var sizeCategory
    var size: CGFloat
    
    func body(content: Content) -> some View {
        let scaledSize = UIFontMetrics.default.scaledValue(for: size)
        return content.font(.system(size: scaledSize))
    }
}

extension View {
    func scaledFont(size: CGFloat) -> some View {
        return self.modifier(ScaledFont(size: size))
    }
}

Usage example:

var body: some View {
    Text("Hello, World!").scaledFont(size: 18)
}

You can use the Environment Overrides panel on Xcode 12 to test it.