I’ve added custom fonts to my project and have successfully been able to use each font to stylize text but only in a settings menu:
class AppearanceManager: ObservableObject {
@Published var Font: String = "System" {
didSet {
saveFont()
objectWillChange.send()
print("Set font to \(Font)")
print("Corresponding font name: \(fonts[Font])")
}
}
public let fonts = ["System" : "system-default", // unrecognized name defaults to system font
"Courier Prime" : "CourierPrime-Regular",
"JetBrains Mono" : "JetBrainsMono-Regular",
"Roboto" : "RobotoMono-Regular",
"Ubuntu" : "UbuntuMono-Regular",
"Victor" : "VictorMono-Regular",
"Times New Roman" : "TimesNewRomanPSMT",
"Trebuchet" : "TrebuchetMS"
]
init() {
loadFont()
// used for debugging
for family in UIFont.familyNames.sorted() {
let names = UIFont.fontNames(forFamilyName: family)
print("Family: \(family) Font names: \(names)")
}
}
private let fontKey = "FontKey"
private func saveFont() {
UserDefaults.standard.set(Font, forKey: fontKey)
}
private func loadFont() {
if let savedFont = UserDefaults.standard.value(forKey: fontKey) as? String {
Font = savedFont
}
}
}
struct SettingsView: View {
@EnvironmentObject var appearanceManager: AppearanceManager
var body: some View {
Form {
FontSelectionView()
}
.navigationTitle("Settings")
}
}
struct FontSelectionView: View {
@EnvironmentObject var appearanceManager: AppearanceManager
var body: some View {
Section(header: Text("Fonts")) {
VStack(alignment: .leading) {
ForEach(appearanceManager.fonts.keys.sorted(), id: \.self) { fontName in
FontButton(name: fontName)
}
.padding(.vertical, 5)
}
}
}
}
struct FontButton: View {
@EnvironmentObject var appearanceManager: AppearanceManager
let name: String
var isSelected: Bool {
appearanceManager.Font == name
}
var body: some View {
HStack {
Text(name)
.font(.custom(appearanceManager.fonts[name] ?? "", size: 17, relativeTo: .body))
Spacer()
Circle()
.frame(width: 20, height: 20)
.foregroundColor(isSelected ? .secondary : .clear)
.overlay(
Circle()
.stroke(Color.primary, lineWidth: 2)
)
}
.foregroundColor(.secondary)
.onTapGesture {
withAnimation {
appearanceManager.Font = name
}
}
}
}
The code above can be used to apply each font to text:

However certain fonts (not just custom fonts I added to my project, but also built-in fonts such as Trebuchet) are not being applied to text elements throughout my app:
struct PostView: View {
@EnvironmentObject var appearanceManager: AppearanceManager
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
Text("Hello World")
// below only works for certain fonts
.font(.custom(appearanceManager.Font, size: 30, relativeTo: .title))
}
.padding()
}
}
}
I have injected the EnvironmentObject to the root view of my app
@main
struct MyApp: App {
@StateObject private var appearanceManager = AppearanceManager()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(appearanceManager)
}
}
}
I have also printed all available fonts and determined that the actual font name is being published:
// prints in init() method:
...
Family: Times New Roman Font names: ["TimesNewRomanPSMT", "TimesNewRomanPS-ItalicMT", "TimesNewRomanPS-BoldMT", "TimesNewRomanPS-BoldItalicMT"]
Family: Trebuchet MS Font names: ["TrebuchetMS", "TrebuchetMS-Italic", "TrebuchetMS-Bold", "Trebuchet-BoldItalic"]
Family: Ubuntu Mono Font names: ["UbuntuMono-Regular"]
Family: Verdana Font names: ["Verdana", "Verdana-Italic", "Verdana-Bold", "Verdana-BoldItalic"]
Family: Victor Mono Font names: ["VictorMono-Regular"]
...
// prints when I set a font:
Set font to Trebuchet
Corresponding font name: Optional("TrebuchetMS") // Not applied outside of settings menu
Set font to JetBrains Mono
Corresponding font name: Optional("JetBrainsMono-Regular") // works as expected
Set font to Roboto
Corresponding font name: Optional("RobotoMono-Regular") // not applied outside of settings menu




