9

I'm trying to change the default font in SwiftUI for every view in my app.

What I want to avoid is to set it every time like so:

.font(.custom("FONT_NAME", size: 20))

What I want is to change it just one time for all Text views, and most important, I would like to keep using the modifiers:

.font(.caption)

without resetting the Text view to the system font.

But still I didn't find any solution, does anyone have one?

2 Answers 2

5

You can build your own view modifier. You can define what size and font you want to use for each of the UIFont.TextStyle inside private var fontDescriptions:.

Now you can call this modifier on you Views like this. Text("my text").customFont(.headline)

Keep in mind, this one also implements the font scaling feature.

You could also call your "customFont" function "font" if you want to be hardcore.

extension View {
    func customFont(_ textStyle: UIFont.TextStyle) -> ModifiedContent<Self, CustomFont> {
        return modifier(CustomFont(textStyle: textStyle))
    }
}

struct CustomFont: ViewModifier {
    let textStyle: UIFont.TextStyle

    /// Will trigger the refresh of the view when the ContentSizeCategory changes.
    @Environment(\.sizeCategory) var sizeCategory: ContentSizeCategory

    func body(content: Content) -> some View {

        guard let fontDescription = fontDescriptions[textStyle] else {

            print("textStyle nicht vorhanden: \(textStyle)")

            return content.font(.system(.body));
        }

        let fontMetrics = UIFontMetrics(forTextStyle: textStyle)
        let fontSize = fontMetrics.scaledValue(for: fontDescription.1)

        return content.font(.custom(fontDescription.0, size: fontSize))
    }
}

/// Define the custom fonts to use, depending on the TextStyle.
typealias CustomFontDescription = (String, CGFloat)
private var fontDescriptions: [UIFont.TextStyle: CustomFontDescription] = [
    .headline: ("MYFONT", SIZE),
    .subheadline: ("MYFONT", SIZE),
    ...
]
Sign up to request clarification or add additional context in comments.

2 Comments

I spent all day searching for a solution that would dynamically scale fonts as you change them and not just set them on load. This was the only solution that worked for me. Well done!
Did you mean to use Font.custom(_ name:, fixedSize:)? because custom(_ name: , size:) scales text automatically with body size. In iOS 14 there is a method custom(_ name:size:relativeTo textStyle:) -> Font
0

You can use a view modifier to solve your case.

@available(iOS 13.0, *)
struct CustomText: ViewModifier {
    func body(content: Content) -> some View {
        content
            .font(.custom("FONT_NAME", size: textSize(textStyle: .headline)))
            .background(Color.blue)
            .foregroundColor(Color.white)
           //Other customization
    }
}
//For Accessibility feature
func textSize(textStyle: UIFont.TextStyle) -> CGFloat {
   return UIFont.preferredFont(forTextStyle: textStyle).pointSize
}
@available(iOS 13.0, *)
struct ContentView: View {
    var body: some View {
        Text("Hello, SwiftUI")
            .modifier(CustomText())
            .font(.caption) // You can still change the font .
    }
}

@available(iOS 13.0, *)
struct SwiftUIView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

2 Comments

This one is lacking the accessibility features for text. I thing merging our both answers is the right answer. Mine for overriding .font(.caption) and yours for setting the default font via body modifier.
I have updated my answer to support accessibility feature @Mamaessen

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.