1

Aim:

I would like to know how to use a system font in my SwiftUI app with a custom size that supports dynamic size (that changes when the user changes the text size on the device, font size changes dynamically).

Problem:

I can use the standard .body, .title etc for system fonts, however I want to use a custom size which is slightly different however still want to make sure it works with dynamic types

My Failed Attempt:

//Font size doesn't change when the user changes his preferred text size on the device
Text("hello")
    .font(.system(size: 14, weight: .bold, design: .default))

Note:

I am targeting iOS 15, I am looking for something on the lines of the following for system font:

static func custom(_ name: String, size: CGFloat, relativeTo textStyle: Font.TextStyle) -> Font

Questions:

  • Is there a way to specify the system font name in the above API? If so what is the font name for iOS 15 and macOS Monterey?
  • Is there any other way to achieve dynamic fonts for system font with custom sizes?

2 Answers 2

1

Following Font function helps achieve custom font size relative to TextStyle:

static func custom(
    _ name: String,
    size: CGFloat,
    relativeTo textStyle: Font.TextStyle
) -> Font
Sign up to request clarification or add additional context in comments.

Comments

0

I made it with somewhat hacky font size estimation based on current sizeCategory:


public enum AppTextStyle {
    case veryCustomTextStyle
    case anotherCustomTextStyle
}

struct ScaledFontModifier: ViewModifier {
    
    @Environment(\.sizeCategory) var sizeCategory
    var textStyle: AppTextStyle

    func body(content: Content) -> some View {
        content.font(font(for: textStyle))
    }
    
    private func font(for style: AppTextStyle) -> Font {
        switch style {
        case .veryCustomTextStyle:
            return .system(size: scaledValue(for: 42))
        case .anotherCustomTextStyle:
            return .system(size: scaledValue(for: 42), weight: .medium)
        }
    }

    private func scaledValue(for size: CGFloat) -> CGFloat {
        switch sizeCategory {
        case .extraSmall:
            return size - 3
        case .small:
            return size - 2
        case .medium:
            return size - 1
        case .large:
            return size
        case .extraLarge:
            return size + 2
        case .extraExtraLarge:
            return size + 4
        case .extraExtraExtraLarge:
            return size + 6
        case .accessibilityMedium:
            return size + 11
        case .accessibilityLarge:
            return size + 16
        case .accessibilityExtraLarge:
            return size + 23
        case .accessibilityExtraExtraLarge:
            return size + 30
        case .accessibilityExtraExtraExtraLarge:
            return size + 36
        @unknown default:
            return size
        }
    }
}

public extension View {
    func font(_ textStyle: AppTextStyle) -> some View {
        modifier(ScaledFontModifier(textStyle: textStyle))
    }
}

You can use it along the built-in fonts, or you can put all your app text styles in AppTextStyle enum:

Text("Hello")
    .font(.body)
Text("World")
    .font(.veryCustomTextStyle)

3 Comments

Thanks for the answer, there is a new API to achieve this conveniently static func custom(_ name: String, size: CGFloat, relativeTo textStyle: Font.TextStyle) -> Font
Thank you! What if I want a system font, but custom size? I can hardcode ".SFUI-Semibold", but it might broke in the future :(
Be aware that if you use that modified you will loose the capability to use Dynamic Text in Previews. stackoverflow.com/questions/78796708/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.