swiftswiftuiios13ios14dynamic-type-feature

Fixing the size of a custom font in SwiftUI iOS 13+ when ignoring Dynamic Type


In an app I'm developing (SwiftUI for iOS13 and above), I have imported a custom font, and I load the font using the following method":

func getDigitalXFontOfSize(size s : CGFloat) -> Font {
        return Font.custom("DigitalX", size: s);
}

I have a Text object in a view as follows:

let font : Font = FontGetter.getDigitalXFontOfSize(20.0);

var body: some View {
        HStack {
            Text("some text").font(FontGetter)
        }
}

In iOS, there are options to change the font size in Settings, and it states: "Apps that support Dynamic Type will adjust to your preferred reading size below". And as far as I am aware, the Text view in SwiftUI automatically adopts dynamic scaling of fonts based on the Dynamic Type value.

Now I understand that accessibility is of great importance and if a user wants a bigger font they should be able to get one etc. My app is an add-on for a network game and requires a very specific layout that doesn't adhere to very large nor very small text (like those available in iOS such as accessibilityExtraExtraExtraLarge)

Some apps, like the Reddit app for iOS, seem to ignore the text size set in the Settings App completely.

When you change the text size slider in the Settings app, the text size also changes in my app, which I do not want to happen.

Can I turn off Dynamic Type support in my app and always render the fonts the exact size I specified them? If so, how can it be done?

Can I disable Dynamic Type for specific Text views that I use in the app?

I have spent a many hours searching SO for similar issues, but I can't seem to find a solution for iOS 13 and above or SwiftUI. Previous version of iOS (or swift) didn't scale custom fonts created in the same way, but now they seem to.

EDIT

In iOS 14, there is an option to create a fixed font size as follows:

func getDigitalXFontOfSize(size s : CGFloat) -> Font {
        return Font.custom("DigitalX", fixedSize: s);
}

which seems to work. But I also require something like this for iOS 13 also.


Solution

  • A small hack

    It seems Apple does not give us a direct way to influence the max size of the dynamic fonts.

    To avoid very small font sizes there is the minimumScaleFactor modifier like

    .minimumScaleFactor(0.5)
    

    To avoid very large fonts it is not so easy.

    What I did in my app is to get the Environment variable called sizeCategory and depending on the value of this system variable, adjusting my font.

    It should work for you as well. In your view, you would add the environment variable in your struct. Then if you know that the .accessibilityExtraExtraLarge breaks your layout you can adjust it like this:

    struct ContentView: View {
        @Environment(\.sizeCategory) var sizeCategory
    
        var body: some View {
           Text("Hello World")
             .font(sizeCategory == .accessibilityExtraExtraLarge ?
               .custom("Helvetica", size: 23, relativeTo: .headline) :      
               .custom("Helvetica", size: 33, relativeTo: .headline))
             .minimumScaleFactor(0.5)
        }
    }
    

    I do not have your full code to test, but I would add this to your function it should work:

    func getDigitalXFontOfSize(size s : CGFloat) -> Font {
        if font(sizeCategory == .accessibilityExtraExtraLarge {
            return Font.custom("DigitalX", size: s - 10)
        } else {
            return Font.custom("DigitalX", size: s)
    }
    

    I hope this would help. I guess using a switch statement inside the function could be an idea as well!