Swift中Presenter/ViewModel生成的UI模型如何适配暗黑模式动态更新颜色
问题根因
颜色无法动态更新的核心原因有两个:
- 自定义的
.appBlack为静态固化色值,并非系统支持的动态颜色,实例创建时就固定了当前主题下的色值,不会随系统主题切换自动变更 - 结构体
TextRow在ViewModel层提前初始化时,将固定色值存为属性,后续调用tableView.reloadData()时,读取的始终是初始化阶段存储的旧色值,无法获取新主题下的对应颜色。
修复方案
按优先级从高到低操作即可实现暗黑/浅色模式的自动适配:
第一步:将自定义颜色改为系统动态颜色
不要用静态赋值写死色值,通过UIColor的动态构造方法实现,示例代码:extension UIColor { static let appBlack = UIColor(dynamicProvider: { trait in switch trait.userInterfaceStyle { case .dark: return UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1) // 替换为深色模式下设计要求的文本色值 default: return UIColor.black } }) }不要在颜色定义阶段提前读取
UITraitCollection.current的色值存储,必须通过dynamicProvider闭包返回对应模式下的颜色,系统会在每次读取颜色时自动触发闭包,匹配当前主题返回正确色值。第二步:调整模型存储逻辑
确保TextRow的color属性直接存储UIColor类型的动态实例,不要在模型初始化阶段做任何颜色解析、转CGColor、提前取色值的操作,原有ViewModel创建实例的代码不需要修改:TextRow(title: "Come", font: .myFont(.bold, size: 14), color: .appBlack, alignment: .center)第三步:Cell内动态解析颜色赋值
在Cell的配置方法中,基于Cell当前的traitCollection解析颜色后再赋值给控件,不要在Cell初始化阶段一次性写死颜色:func config(row: TextRow) { titleLabel.text = row.title titleLabel.font = row.font titleLabel.textAlignment = row.alignment // 基于当前Cell的特征集合解析对应主题下的颜色 titleLabel.textColor = row.color.resolvedColor(with: self.traitCollection) }第四步:正确监听主题变更刷新列表
不需要在ViewModel层做监听,直接在对应UITableViewController/Cell内重写traitCollectionDidChange方法,判断主题变化后刷新可见单元格即可:override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) guard traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) else { return } // 仅刷新可见单元格,性能开销更低 tableView.reloadRows(at: tableView.indexPathsForVisibleRows ?? [], with: .none) }
如果自定义字体
myFont也需要做不同主题下的适配(比如不同字重、字号),可以参考颜色的适配逻辑,通过动态提供者的方式实现,不要存储静态值。
内容的提问来源于stack exchange,提问作者ryla
相关产品推荐
相关产品推荐

