如何在@AppStorage中存储Font?实现RawRepresentable及字体家族修改
问题解答
一、为SwiftUI的Font实现RawRepresentable协议
要让Font支持AppStorage,需实现RawRepresentable协议,将字体的家族名和字号序列化为字符串,再从字符串反序列化为Font:
import SwiftUI import CoreText extension Font: RawRepresentable { public typealias RawValue = String // 从字符串反序列化为Font public init?(rawValue: RawValue) { let parts = rawValue.components(separatedBy: ",") guard parts.count == 2, let size = Double(parts[1]) else { return nil } let familyName = parts[0] self = Font.custom(familyName, size: CGFloat(size)) } // 将Font序列化为字符串 public var rawValue: RawValue { guard let ctFont = self as? CTFont else { return "" } let familyName = CTFontCopyFamilyName(ctFont) as String let size = CTFontGetSize(ctFont) return "\(familyName),\(size)" } }
说明:
- 借助CoreText的
CTFont获取字体核心信息,SwiftUI的Font底层封装了CTFont - 序列化格式为
"家族名,字号",解析时拆分字符串并验证格式有效性 - 若序列化失败(如系统内置字体无法转为
CTFont),返回空字符串,反序列化时会返回nil
二、仅修改字体家族,保留原有字号
当前代码固定了字号18,要实现只切换字体家族、保留原有字号的效果,可按以下方式修改:
1. 为Font添加扩展获取字号
extension Font { // 获取字体的字号(基于CoreText) var fontSize: CGFloat? { (self as? CTFont).map { CGFloat(CTFontGetSize($0)) } } }
2. 修改FontPicker中的字体选择逻辑
在sheet的SUIFontPicker回调里,提取选中字体的家族名,使用当前的字号(优先自定义字体的字号,其次是环境默认字体的字号,最后 fallback 到18):
.sheet(isPresented: $isFontPickerPresented) { SUIFontPicker { fontDescriptor in guard let familyName = fontDescriptor.familyName else { return } // 确定要使用的字号 let targetSize = vm.font?.fontSize ?? defaultFont?.fontSize ?? 18 // 创建仅修改家族的Font vm.font = Font.custom(familyName, size: targetSize) printDetails() } }
说明:
- 从
UIFontDescriptor中提取familyName,即用户选择的字体家族 - 优先沿用已保存的自定义字体字号,若没有则使用环境默认字体的字号,确保字号与之前保持一致
- 直接使用SwiftUI的
Font.custom创建字体,更贴合SwiftUI生态,无需依赖UIFont
修改后,用户选择字体时只会切换字体家族,字号将保留当前文本使用的大小,无需固定指定数值。
内容的提问来源于stack exchange,提问作者BPDev
相关产品推荐
相关产品推荐

