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)
            }
    }

}
Issues Supporting All Accessibility Features with a Custom Font
 
 
Q