import CoreText import SwiftUI import UIKit // 品牌字体接入(对齐 web 字体栈 --font-sans / --font-mono): // - Sans 正文 = Orbit Gothic CJK SC(Regular 400 + SemiBold 600 两变体) // - Mono = Maple Mono(拉丁 / 符号) // 字体以 woff2 内嵌(体积约 TTF 的 1/3;iOS Core Text 自 iOS 13 起原生支持 woff/woff2),故经 // CTFontManagerRegisterFontsForURL 运行期注册(UIAppFonts 历史上只认 ttf/otf,woff2 走运行期最稳)。 // 仅主 app 接入——CJK 字体大(~10MB/字重),不进 Share / 控件扩展(它们保持系统字 + 仅 Fredoka 字标)。 // 生僻字(超出 Orbit Gothic 字汇)由 iOS 字体级联自动回退系统苹方,不出豆腐块。 enum BrandFonts { static let sansRegular = "OrbitGothicCJKsc-Regular" static let sansSemibold = "OrbitGothicCJKsc-SemiBold" static let mono = "MapleMono-Regular" // app 启动最早期(AppDelegate didFinishLaunching)调用一次:注册字体 + 设 UIKit 外观代理。 static func register() { for name in [ sansRegular, sansSemibold, mono ] { guard let url = Bundle.main.url(forResource: name, withExtension: "woff2") else { continue } CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil) } applyUIKitAppearance() } // SwiftUI 的导航标题 / 标签栏标题经 UIKit 渲染,SwiftUI 无 API 直改,故经 appearance 代理统一换 Orbit。 private static func applyUIKitAppearance() { if let large = UIFont(name: sansSemibold, size: 32) { UINavigationBar.appearance().largeTitleTextAttributes = [ .font: large ] } if let inline = UIFont(name: sansSemibold, size: 17) { UINavigationBar.appearance().titleTextAttributes = [ .font: inline ] } if let tab = UIFont(name: sansRegular, size: 10) { let item = UITabBarItem.appearance() item.setTitleTextAttributes([ .font: tab ], for: .normal) item.setTitleTextAttributes([ .font: tab ], for: .selected) } } } // 字体助手:Orbit Gothic(Sans)+ Maple Mono(Mono)。relativeTo 让自定义字体随动态字号缩放 // (不丢可访问性)。weight 仅二档——bold 走 SemiBold 变体,其余走 Regular(再粗由系统合成)。 extension Font { static func orbit(_ size: CGFloat, bold: Bool = false, relativeTo style: Font.TextStyle = .body) -> Font { .custom(bold ? BrandFonts.sansSemibold : BrandFonts.sansRegular, size: size, relativeTo: style) } static func maple(_ size: CGFloat, relativeTo style: Font.TextStyle = .body) -> Font { .custom(BrandFonts.mono, size: size, relativeTo: style) } // 与 SF 语义文本样式对位的 Orbit 版本(字号 / 字重对齐 SF 默认值),供全局替换 .font(.xxx)。 // headline / title* 默认偏粗 → SemiBold;正文 / 说明类 → Regular。 static let oLargeTitle = orbit(34, bold: true, relativeTo: .largeTitle) static let oTitle = orbit(28, bold: true, relativeTo: .title) static let oTitle2 = orbit(22, bold: true, relativeTo: .title2) static let oTitle3 = orbit(20, bold: true, relativeTo: .title3) static let oHeadline = orbit(17, bold: true, relativeTo: .headline) static let oBody = orbit(17, relativeTo: .body) static let oCallout = orbit(16, relativeTo: .callout) static let oSubheadline = orbit(15, relativeTo: .subheadline) static let oFootnote = orbit(13, relativeTo: .footnote) static let oCaption = orbit(12, relativeTo: .caption) static let oCaption2 = orbit(11, relativeTo: .caption2) // Mono:等宽场景(日志行 / 设备 id / 字节计数)。 static let mCaption = maple(12, relativeTo: .caption) static let mFootnote = maple(13, relativeTo: .footnote) static let mBody = maple(15, relativeTo: .body) }