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