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