-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathSTFontManager.swift
More file actions
265 lines (239 loc) · 11.4 KB
/
Copy pathSTFontManager.swift
File metadata and controls
265 lines (239 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
//
// STFontManager.swift
// STBaseProject
//
// Created by 寒江孤影 on 2018/10/10.
//
import UIKit
public struct STFontFamilyConfig: Sendable, Equatable {
public var regular: String?
public var medium: String?
public var semibold: String?
public var bold: String?
public var light: String?
public var ultraLight: String?
public var thin: String?
public var heavy: String?
public var black: String?
/// 使用系统默认字体(简体中文环境下系统会自动选用苹方)
public static let system = STFontFamilyConfig()
public init(
regular: String? = nil,
medium: String? = nil,
semibold: String? = nil,
bold: String? = nil,
light: String? = nil,
ultraLight: String? = nil,
thin: String? = nil,
heavy: String? = nil,
black: String? = nil
) {
self.regular = regular
self.medium = medium
self.semibold = semibold
self.bold = bold
self.light = light
self.ultraLight = ultraLight
self.thin = thin
self.heavy = heavy
self.black = black
}
func fontName(for weight: UIFont.Weight) -> String? {
switch weight {
case .regular: return regular
case .medium: return medium
case .semibold: return semibold
case .bold: return bold
case .light: return light
case .ultraLight: return ultraLight
case .thin: return thin
case .heavy: return heavy
case .black: return black
default: return nil
}
}
}
// MARK: - STFontManager
public final class STFontManager {
public static let shared = STFontManager()
private let configurationLock = NSLock()
private var storedFontFamily: STFontFamilyConfig = .system
private var storedFontSizeScale: CGFloat = 1.0
public var fontFamily: STFontFamilyConfig {
self.configurationLock.lock()
defer { self.configurationLock.unlock() }
return self.storedFontFamily
}
/// 全局字体缩放比例(用于字号调节功能),默认 1.0。
/// 设置后所有 `st_systemFont` / `st_preferredFont` 方法均会自动乘以此比例。
public var fontSizeScale: CGFloat {
get {
self.configurationLock.lock()
defer { self.configurationLock.unlock() }
return self.storedFontSizeScale
}
set {
self.configurationLock.lock()
defer { self.configurationLock.unlock() }
self.storedFontSizeScale = newValue
}
}
private init() {}
public func configure(fontFamily: STFontFamilyConfig) {
self.configurationLock.lock()
defer { self.configurationLock.unlock() }
self.storedFontFamily = fontFamily
}
/// 同时配置字体族和缩放比例。
public func configure(fontFamily: STFontFamilyConfig, fontSizeScale: CGFloat) {
self.configurationLock.lock()
defer { self.configurationLock.unlock() }
self.storedFontFamily = fontFamily
self.storedFontSizeScale = fontSizeScale
}
public func reset() {
self.configurationLock.lock()
defer { self.configurationLock.unlock() }
self.storedFontFamily = .system
self.storedFontSizeScale = 1.0
}
}
// MARK: - UIFontMetrics 方案(支持 Dynamic Type)
public extension UIFont {
/// 使用自定义字体族 + UIFontMetrics 缩放,支持 Dynamic Type
/// 迁移时只需: UIFont.preferredFont(forTextStyle: .body) → UIFont.st_preferredFont(ofSize: 14, forTextStyle: .body)
/// - Parameters:
/// - size: 基准字号(设计稿尺寸)
/// - style: 文本样式,用于 UIFontMetrics 缩放(默认 .body)
/// - weight: 字重(默认 .regular)
/// - maxSize: 最大字号限制(可选)
static func st_preferredFont(ofSize size: CGFloat, forTextStyle style: UIFont.TextStyle = .body, weight: UIFont.Weight = .regular, maxSize: CGFloat? = nil) -> UIFont {
let adjustedSize = size * STFontManager.shared.fontSizeScale
let config = STFontManager.shared.fontFamily
let baseFont: UIFont
if let name = config.fontName(for: weight),
let customFont = UIFont(name: name, size: adjustedSize) {
baseFont = customFont
} else {
baseFont = UIFont.systemFont(ofSize: adjustedSize, weight: weight)
}
let metrics = UIFontMetrics(forTextStyle: style)
if let maxSize = maxSize {
return metrics.scaledFont(for: baseFont, maximumPointSize: maxSize)
}
return metrics.scaledFont(for: baseFont)
}
/// 使用指定字体名 + UIFontMetrics 缩放,支持 Dynamic Type
/// - Parameters:
/// - name: 字体名称
/// - size: 基准字号
/// - style: 文本样式,用于 UIFontMetrics 缩放(默认 .body)
/// - maxSize: 最大字号限制(可选)
static func st_preferredFont(name: String, ofSize size: CGFloat, forTextStyle style: UIFont.TextStyle = .body, maxSize: CGFloat? = nil) -> UIFont {
let adjustedSize = size * STFontManager.shared.fontSizeScale
let baseFont = UIFont(name: name, size: adjustedSize) ?? .systemFont(ofSize: adjustedSize)
let metrics = UIFontMetrics(forTextStyle: style)
if let maxSize = maxSize {
return metrics.scaledFont(for: baseFont, maximumPointSize: maxSize)
}
return metrics.scaledFont(for: baseFont)
}
}
// MARK: - 便捷方法(与 UIFont.systemFont 签名一致,方便替换)
public extension UIFont {
/// 替换 UIFont.systemFont(ofSize:)
/// 使用自定义字体族 + 屏幕适配缩放 + 全局 fontSizeScale
/// 迁移时只需: UIFont.systemFont(ofSize: 14) → UIFont.st_systemFont(ofSize: 14)
static func st_systemFont(ofSize size: CGFloat) -> UIFont {
let scaledSize = STDeviceAdapter.scaledWidth(size * STFontManager.shared.fontSizeScale)
let config = STFontManager.shared.fontFamily
if let name = config.fontName(for: .regular),
let font = UIFont(name: name, size: scaledSize) {
return font
}
return .systemFont(ofSize: scaledSize)
}
/// 替换 UIFont.systemFont(ofSize:weight:)
/// 迁移时只需: UIFont.systemFont(ofSize: 14, weight: .medium) → UIFont.st_systemFont(ofSize: 14, weight: .medium)
static func st_systemFont(ofSize size: CGFloat, weight: UIFont.Weight) -> UIFont {
let scaledSize = STDeviceAdapter.scaledWidth(size * STFontManager.shared.fontSizeScale)
let config = STFontManager.shared.fontFamily
if let name = config.fontName(for: weight),
let font = UIFont(name: name, size: scaledSize) {
return font
}
return UIFont.systemFont(ofSize: scaledSize, weight: weight)
}
/// 替换 UIFont.boldSystemFont(ofSize:)
/// 迁移时只需: UIFont.boldSystemFont(ofSize: 14) → UIFont.st_boldSystemFont(ofSize: 14)
static func st_boldSystemFont(ofSize size: CGFloat) -> UIFont {
let scaledSize = STDeviceAdapter.scaledWidth(size * STFontManager.shared.fontSizeScale)
let config = STFontManager.shared.fontFamily
if let name = config.fontName(for: .semibold),
let font = UIFont(name: name, size: scaledSize) {
return font
}
return UIFont.boldSystemFont(ofSize: scaledSize)
}
/// 替换 UIFont.italicSystemFont(ofSize:)
/// 迁移时只需: UIFont.italicSystemFont(ofSize: 14) → UIFont.st_italicSystemFont(ofSize: 14)
static func st_italicSystemFont(ofSize size: CGFloat) -> UIFont {
let scaledSize = STDeviceAdapter.scaledWidth(size * STFontManager.shared.fontSizeScale)
return UIFont.italicSystemFont(ofSize: scaledSize)
}
/// 替换 UIFont.preferredFont(forTextStyle:)
/// 使用自定义字体族 + UIFontMetrics 缩放,支持 Dynamic Type
/// 字号由系统 TextStyle 自动决定,无需手动传入
/// 迁移时只需: UIFont.preferredFont(forTextStyle: .body) → UIFont.st_preferredFont(forTextStyle: .body)
static func st_preferredFont(forTextStyle style: UIFont.TextStyle, weight: UIFont.Weight = .regular) -> UIFont {
let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: style)
let pointSize = descriptor.pointSize
return st_preferredFont(ofSize: pointSize, forTextStyle: style, weight: weight)
}
/// 替换 UIFont.monospacedDigitSystemFont(ofSize:weight:)
/// 等宽数字字体,适用于计时器、价格等需要数字对齐的场景
/// 迁移时只需: UIFont.monospacedDigitSystemFont(ofSize: 14, weight: .regular) → UIFont.st_monospacedDigitSystemFont(ofSize: 14, weight: .regular)
static func st_monospacedDigitSystemFont(ofSize size: CGFloat, weight: UIFont.Weight) -> UIFont {
let scaledSize = STDeviceAdapter.scaledWidth(size * STFontManager.shared.fontSizeScale)
return .monospacedDigitSystemFont(ofSize: scaledSize, weight: weight)
}
/// 替换 UIFont.monospacedSystemFont(ofSize:weight:)
/// 等宽字体,适用于代码块、终端等需要等宽排列的场景
/// 迁移时只需: UIFont.monospacedSystemFont(ofSize: 14, weight: .regular) → UIFont.st_monospacedSystemFont(ofSize: 14, weight: .regular)
static func st_monospacedSystemFont(ofSize size: CGFloat, weight: UIFont.Weight) -> UIFont {
let scaledSize = STDeviceAdapter.scaledWidth(size * STFontManager.shared.fontSizeScale)
return .monospacedSystemFont(ofSize: scaledSize, weight: weight)
}
// MARK: - 不读取全局 fontSizeScale 的显式 scale 构造入口
/// 与 st_systemFont(ofSize:) 等价,但使用显式传入的 explicitScale 而非 STFontManager.shared.fontSizeScale。
/// 用于字体必须脱离全局可变状态(避免 withFontScale 异步逃逸导致延迟渲染被静默重置为标准字号)的场景。
static func st_systemFont(ofSize size: CGFloat, explicitScale scale: CGFloat) -> UIFont {
let scaledSize = STDeviceAdapter.scaledWidth(size * scale)
let config = STFontManager.shared.fontFamily
if let name = config.fontName(for: .regular),
let font = UIFont(name: name, size: scaledSize) {
return font
}
return .systemFont(ofSize: scaledSize)
}
/// 与 st_systemFont(ofSize:weight:) 等价,但使用显式传入的 explicitScale 而非 STFontManager.shared.fontSizeScale。
static func st_systemFont(ofSize size: CGFloat, weight: UIFont.Weight, explicitScale scale: CGFloat) -> UIFont {
let scaledSize = STDeviceAdapter.scaledWidth(size * scale)
let config = STFontManager.shared.fontFamily
if let name = config.fontName(for: weight),
let font = UIFont(name: name, size: scaledSize) {
return font
}
return UIFont.systemFont(ofSize: scaledSize, weight: weight)
}
/// 与 st_boldSystemFont(ofSize:) 等价,但使用显式传入的 explicitScale 而非 STFontManager.shared.fontSizeScale。
static func st_boldSystemFont(ofSize size: CGFloat, explicitScale scale: CGFloat) -> UIFont {
let scaledSize = STDeviceAdapter.scaledWidth(size * scale)
let config = STFontManager.shared.fontFamily
if let name = config.fontName(for: .semibold),
let font = UIFont(name: name, size: scaledSize) {
return font
}
return UIFont.boldSystemFont(ofSize: scaledSize)
}
}