SwiftUI应用如何全局覆盖默认字体?支持内置样式与导航栏字体修改
解决SwiftUI全局自定义字体的完整方案
前提:确保自定义字体已正确导入项目
- 将字体文件(如
RobotoMono-Regular.ttf、RobotoMono-Bold.ttf)拖入Xcode,勾选"Copy items if needed"和对应target。 - 在
Info.plist中添加Fonts provided by application数组,将每个字体的完整文件名(包括后缀)加入数组。
1. 扩展Font,替换内置样式为自定义字体
通过扩展Font结构体,覆盖SwiftUI内置的字体样式(如.title、.body),让它们自动映射到你的自定义字体,同时支持动态类型(字体大小随系统设置调整):
import SwiftUI extension Font { // 对应SwiftUI内置的所有字体样式,按需覆盖 static var title: Font { custom("RobotoMono-Bold", size: UIFont.preferredFont(forTextStyle: .largeTitle).pointSize) } static var title2: Font { custom("RobotoMono-Bold", size: UIFont.preferredFont(forTextStyle: .title1).pointSize) } static var title3: Font { custom("RobotoMono-Bold", size: UIFont.preferredFont(forTextStyle: .title2).pointSize) } static var headline: Font { custom("RobotoMono-Regular", size: UIFont.preferredFont(forTextStyle: .headline).pointSize) } static var body: Font { custom("RobotoMono-Regular", size: UIFont.preferredFont(forTextStyle: .body).pointSize) } static var callout: Font { custom("RobotoMono-Regular", size: UIFont.preferredFont(forTextStyle: .callout).pointSize) } static var footnote: Font { custom("RobotoMono-Regular", size: UIFont.preferredFont(forTextStyle: .footnote).pointSize) } static var caption: Font { custom("RobotoMono-Regular", size: UIFont.preferredFont(forTextStyle: .caption1).pointSize) } }
2. 全局设置默认字体
在App的根视图通过环境变量,将扩展后的.body字体设为全局默认,所有未显式设置字体的Text都会自动使用自定义字体:
@main struct CustomFontDemoApp: App { var body: some Scene { WindowGroup { ContentView() .environment(\.font, Font.body) // 全局默认字体 } } }
此时直接使用Text("Hello World")会自动应用自定义body字体;需要其他样式时,直接写Text("Headline").font(.headline)即可,完全兼容SwiftUI内置样式语法。
3. 自定义导航栏与标签栏字体
SwiftUI的导航栏、标签栏底层基于UIKit控件,无法通过SwiftUI环境变量修改字体,需通过UIAppearance全局配置:
@main struct CustomFontDemoApp: App { init() { // 配置导航栏字体 let navBarAppearance = UINavigationBarAppearance() // 普通标题字体 navBarAppearance.titleTextAttributes = [ .font: UIFont(name: "RobotoMono-Bold", size: UIFont.preferredFont(forTextStyle: .title1).pointSize)! ] // 大标题字体 navBarAppearance.largeTitleTextAttributes = [ .font: UIFont(name: "RobotoMono-Bold", size: UIFont.preferredFont(forTextStyle: .largeTitle).pointSize)! ] UINavigationBar.appearance().standardAppearance = navBarAppearance UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance // 配置标签栏字体 let tabBarAppearance = UITabBarAppearance() // 未选中状态字体 tabBarAppearance.stackedLayoutAppearance.normal.titleTextAttributes = [ .font: UIFont(name: "RobotoMono-Regular", size: UIFont.preferredFont(forTextStyle: .caption1).pointSize)! ] // 选中状态字体 tabBarAppearance.stackedLayoutAppearance.selected.titleTextAttributes = [ .font: UIFont(name: "RobotoMono-Bold", size: UIFont.preferredFont(forTextStyle: .caption1).pointSize)! ] UITabBar.appearance().standardAppearance = tabBarAppearance if #available(iOS 15.0, *) { UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance } } var body: some Scene { WindowGroup { NavigationStack { TabView { ContentView() .tabItem { Text("Home") } } .navigationTitle("Custom Font Demo") } .environment(\.font, Font.body) } } }
核心原理说明
- 扩展
Font是为了复用SwiftUI的内置样式语法,同时替换为自定义字体,避免逐个控件设置修饰符。 - 全局环境变量
.environment(\.font, ...)让所有子视图默认使用自定义字体,无需重复设置。 - 导航栏/标签栏属于UIKit层级,必须通过
UIAppearance全局配置,这是SwiftUI目前无法直接覆盖的部分。
内容的提问来源于stack exchange,提问作者esbenr
相关产品推荐
相关产品推荐

