Issues Supporting All Accessibility Features with a Custom Font

I am in the process of adding my company's brand font to our SwiftUI app. I am able to implement the font using the provided public APIs so that text styles / dynamic type and the font weight modifier in SwiftUI work correctly.

However we are unable to implement custom font in such a way that text styles / dynamic type, the font weight modifier, and the bold text accessibility setting all work at the same time. Am I missing an implementation detail so that all these features work correctly?

Font Setup

The font files were modified to better support SwiftUI:

  • The font style name metadata was modified to match the name the .fontWeight(...) modifier expects. This was done with Typelight.
  • The font weight value (100/200/300) was modified so that the underlying weight value matches the value the .fontWeight(...) modifier expects. See "Using custom fonts with SwiftUI" by Matthew Flint.
  • The font files were imported via the Info.plist.

Examples

Font Weight Comparison

San Fransisco:

Text("#100")
    .font(.largeTitle)
    .fontWeight(.ultraLight)

Overpass by Name:

Text("#100")
    .font(.custom("Overpass-UltraLight", size: 34, relativeTo: .largeTitle))

Overpass by Weight:

Text("#100")
    .fontWeight(.ultraLight)
    .font(.custom("Overpass", size: 34, relativeTo: .largeTitle))

Legibility Weight Test

When using the .fontWeight(...) modifier, the custom font does not change weights when the bold text accessibility setting is enabled. Dynamic type size works as expected.

Normal legibility weight:

Bold legibility weight:

Dynamic Type Size:

Use UIFont

Using UIFont to load the custom font files and initializing a Font with the UIFont breaks dynamic type:

Bold type also does not work:

Custom Modifier

Creating a custom modifier allows us to support dynamic type and manually handle bold text. However it creates a conflicting API to SwiftUI's .fontWeight(...) modifier.

struct FontModifier: ViewModifier {

    enum UseCase {
        case paragraph
        case headline
    }

    enum Weight {
        case light
        case regular
        case heavy
    }

    @Environment(\.legibilityWeight) var legibilityWeight

    var useCase: UseCase
    var weight: Weight

    init(_ useCase: UseCase, _ weight: Weight) {
        self.useCase = useCase
        self.weight = weight
    }

    var resolvedHeadlineWeight: String {
        let resolvedLegibilityWeight = legibilityWeight ?? .regular

        switch weight {
        case .light:
            switch resolvedLegibilityWeight {
            case .regular:
                return "Light"
            case .bold:
                return "Semibold"
            @unknown default:
                return "Light"
            }
        case .regular:
            switch resolvedLegibilityWeight {
            case .regular:
                return "Regular"
            case .bold:
                return "Bold"
            @unknown default:
                return "Regular"
            }
        case .heavy:
            switch resolvedLegibilityWeight {
            case .regular:
                return "Heavy"
            case .bold:
                return "Black"
            @unknown default:
                return "Heavy"
            }
        }
    }

    var resolvedParagraphWeight: Font.Weight {
        switch weight {
        case .light:
            return .light
        case .regular:
            return .regular
        case .heavy:
            return .heavy
        }
    }

    var resolvedFont: Font {
        switch useCase {
        case .paragraph:
            return .system(.largeTitle).weight(resolvedParagraphWeight)

        case .headline:
            return .custom("Overpass-\(resolvedHeadlineWeight)", size: 34, relativeTo: .largeTitle)
        }
    }

    func body(content: Content) -> some View {
        content
            .font(resolvedFont)
    }

}

GridRow {
    Text("Aa")
        .modifier(FontModifier(.paragraph, .regular))
    Text("Aa")
        .modifier(FontModifier(.paragraph, .heavy))
    Text("Aa")
        .modifier(FontModifier(.headline, .regular))
    Text("Aa")
        .modifier(FontModifier(.headline, .heavy))
}

Font Environment Value

The font environment value does not contain font weight information when the fontWeight(...) modifier is used.:

struct DumpFontTest: View {

    @Environment(\.font) var font

    var body: some View {
        Text("San Fransisco")
            .onAppear {
                print("------------")
                dump(font)
            }
    }

}
Answered by DTS Engineer in 798978022

Thanks for providing such a detailed description of the problem.

Your code demonstrates that, when rendering a piece of text with a custom font, the system doesn't automatically change the font weight based on the Accessibility setting (Settings > Accessibility > Display & Text Size > Bold Text). I believe that is an issue on the system side, and would suggest that you file a feedback report – If you do so, please share your report ID here for folks to track.

Before the system fixes the issue, app developers can consider solving the issue by mapping the legibility weight (LegibilityWeight) to the appropriate font variant with their own code, as shown in your CustomModifierView.

Regarding the issue that the system doesn't automatically adapt the dynamic type size settings (Settings > Accessibility > Display & Text Size > Larger Text) when using UIFont with SwiftUI Text, as demonstrated in your UIFontView and UIFontWithBoldTextView, it is because SwiftUI doesn't update the text when the setting is changed. You can fix that by adding the dynamicTypeSize environment to your view to have SwiftUI observe the change: as shown below:

struct UIFontView: View {
    @Environment(\.dynamicTypeSize) var dynamicTypeSize
    ...
}

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Do you get the same results with just the relevant code in a small test project? If so, please share a link to your test project. That'll help us better understand what's going on. If you're not familiar with preparing a test project, take a look at Creating a test project.

I do. Here's the code sample I used when testing and generating the images in the screenshots of my post:

https://github.com/brandon-mcquilkin-kr/custom-font-sample

Thanks for providing such a detailed description of the problem.

Your code demonstrates that, when rendering a piece of text with a custom font, the system doesn't automatically change the font weight based on the Accessibility setting (Settings > Accessibility > Display & Text Size > Bold Text). I believe that is an issue on the system side, and would suggest that you file a feedback report – If you do so, please share your report ID here for folks to track.

Before the system fixes the issue, app developers can consider solving the issue by mapping the legibility weight (LegibilityWeight) to the appropriate font variant with their own code, as shown in your CustomModifierView.

Regarding the issue that the system doesn't automatically adapt the dynamic type size settings (Settings > Accessibility > Display & Text Size > Larger Text) when using UIFont with SwiftUI Text, as demonstrated in your UIFontView and UIFontWithBoldTextView, it is because SwiftUI doesn't update the text when the setting is changed. You can fix that by adding the dynamicTypeSize environment to your view to have SwiftUI observe the change: as shown below:

struct UIFontView: View {
    @Environment(\.dynamicTypeSize) var dynamicTypeSize
    ...
}

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Issues Supporting All Accessibility Features with a Custom Font
 
 
Q