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

11.9k Views Asked by At

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.

7

There are 7 best solutions below

0
On BEST ANSWER

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!

0
On

Finally, I found a solution that converted UIFont to Font. I have tested it and it works on my devices.

You can use this code on iOS13 and above.

extension Font {
    static let fontName = "Quicksand-Light_Bold"
    static func quickSand(size: CGFloat) -> Font {
        if let uifont =  UIFont(name: fontName, size: size) {
            return Font(uifont as CTFont)
        }
        return Font.custom(fontName, size: size)
    }
}

UIFont to Font

1
On

In case anyone was looking for a system font with fixed size (like me):

.font(.system(size: 8))

(works in iOS 14, I don't know about 13)

0
On

Can I turn off Dynamic Type support […] Can I disable Dynamic Type for specific Text views that I use in the app?

The best way to disable the Dynamic Type feature is to avoid using the text styles.
However, in your case, I strongly recommend to take this feature into account without using its full power (you’ll may need it one day, who knows?).

In addition to the @multitudes accepted answer , it seems interesting to show how the Dynamic Type feature can itself provide this kind of result without being deactivated.

iOS 15 introduced new content size category limits whose goal is to set minimum and maximum sizes with the Dynamic Type feature:

⚠️ But it’s recommended to use it in specific situations as stated in a WWDC 2021 video: ⚠️

Please do not use this API to unduIy limit text size. These settings serve an extremely important function, and it’s paramount that your app’s functionality is all available, and everything is legible, to people using the highest text size setting.

Finally, I suggest to take a look at this interesting article that describes how to restrict the Dynamic Type sizes if need be (including SwiftUI as well).

0
On

Here is what worked for me,

Text("Hello")
            .font((sizeCategory == .extraExtraExtraLarge || sizeCategory == .extraExtraLarge) ?
                       .custom("Helvetica", size: 110) :
                       .custom("Helvetica", size: 130))
            .minimumScaleFactor(0.5)
            .foregroundColor(.white)
2
On

How to disable font scaling entirely on iOS 15.0+

I found out that you can disable this on an app-wide level by setting .dynamicTypeSize(.large) on your root view. (.large seems to be the default).

It seems to behave like .preferredColorScheme where it propagates down to all child views.

So when you have set the dynamicTypeSize on the root view, all fonts where you have specified a fixed font size will no longer scale.

0
On

iOS 15+

Use .dynamicTypeSize(DynamicTypeSize)

Example: restrict to default size

Text("")
    .font(Font.custom("SourceCodePro-Regular", size: 16))
    .dynamicTypeSize(.large) // Forces .large

Example: restrict to default (or smaller)

(If the user has a smaller font size in their iOS settings, this will allow it. If the user picks a BIGGER font, this just forces .large)

Text("")
    .font(Font.custom("SourceCodePro-Regular", size: 16))
    .dynamicTypeSize(...DynamicTypeSize.large) // Use a range

Example: limit in range

(If the user has a bigger/smaller font size in their iOS settings, this will cap it.)

Text("")
    .font(Font.custom("SourceCodePro-Regular", size: 16))
    .dynamicTypeSize(.small ... .accessibility1) // Use a range