Swift MVVM架构下:单视图适配多ViewModel实现差异化样式
视图样式根据ViewModel切换的实现方案
1. 基于协议的ViewModel样式抽象
定义一个包含所有样式属性的协议,让需要控制视图样式的ViewModel都遵守该协议,把样式逻辑内聚到ViewModel中:
// 样式协议 protocol ViewStyling { var buttonBackgroundColor: UIColor { get } var buttonTextColor: UIColor { get } var textViewFont: UIFont { get } var textViewTextColor: UIColor { get } } // ViewModel1实现协议 class ViewModel1: ViewStyling { var buttonBackgroundColor: UIColor { .systemBlue } var buttonTextColor: UIColor { .white } var textViewFont: UIFont { .systemFont(ofSize: 16) } var textViewTextColor: UIColor { .darkText } } // ViewModel2实现协议 class ViewModel2: ViewStyling { var buttonBackgroundColor: UIColor { .systemGreen } var buttonTextColor: UIColor { .black } var textViewFont: UIFont { .boldSystemFont(ofSize: 18) } var textViewTextColor: UIColor { .systemGray } }
在通用视图中,持有协议类型的属性,当属性变化时自动更新样式:
class CommonView: UIView { // 绑定样式协议对象 var styling: ViewStyling? { didSet { updateViewStyles() } } private func updateViewStyles() { guard let styling = styling else { return } button.backgroundColor = styling.buttonBackgroundColor button.setTitleColor(styling.buttonTextColor, for: .normal) textView.font = styling.textViewFont textView.textColor = styling.textViewTextColor } }
使用时直接给视图赋值对应ViewModel的实例即可:
// 绑定ViewModel1样式 let viewWithVM1 = CommonView() viewWithVM1.styling = ViewModel1() // 绑定ViewModel2样式 let viewWithVM2 = CommonView() viewWithVM2.styling = ViewModel2()
2. 独立样式配置+ViewModel映射
把样式抽成独立的配置结构体,通过ViewModel类型匹配对应的配置,实现样式和ViewModel的解耦:
// 样式配置结构体 struct ViewStyleConfig { let buttonBgColor: UIColor let buttonTextColor: UIColor let textViewFont: UIFont let textViewTextColor: UIColor } // 配置映射逻辑 extension ViewStyleConfig { static func config(for viewModel: Any) -> ViewStyleConfig? { switch viewModel { case is ViewModel1: return ViewStyleConfig( buttonBgColor: .systemBlue, buttonTextColor: .white, textViewFont: .systemFont(ofSize: 16), textViewTextColor: .darkText ) case is ViewModel2: return ViewStyleConfig( buttonBgColor: .systemGreen, buttonTextColor: .black, textViewFont: .boldSystemFont(ofSize: 18), textViewTextColor: .systemGray ) default: return nil } } }
视图中监听ViewModel变化,匹配配置更新样式:
class CommonView: UIView { var viewModel: Any? { didSet { applyMatchedStyle() } } private func applyMatchedStyle() { guard let config = ViewStyleConfig.config(for: viewModel) else { return } button.backgroundColor = config.buttonBgColor button.setTitleColor(config.buttonTextColor, for: .normal) textView.font = config.textViewFont textView.textColor = config.textViewTextColor } }
3. 视图内样式策略模式
在视图中针对不同ViewModel定义对应的样式设置方法,根据ViewModel类型调用策略:
class CommonView: UIView { var viewModel: Any? { didSet { applyTargetStyle() } } private func applyTargetStyle() { switch viewModel { case is ViewModel1: setupViewModel1Style() case is ViewModel2: setupViewModel2Style() default: break } } private func setupViewModel1Style() { button.backgroundColor = .systemBlue button.setTitleColor(.white, for: .normal) textView.font = .systemFont(ofSize: 16) textView.textColor = .darkText } private func setupViewModel2Style() { button.backgroundColor = .systemGreen button.setTitleColor(.black, for: .normal) textView.font = .boldSystemFont(ofSize: 18) textView.textColor = .systemGray } }
方案选型建议
- 协议抽象法:最推荐,耦合度最低,新增ViewModel只需实现协议即可,适合样式属性多、ViewModel类型会频繁新增的场景。
- 配置映射法:样式集中管理,便于统一修改,适合ViewModel类型固定、样式需要全局调整的场景。
- 策略模式法:实现简单直接,适合样式逻辑复杂、不同ViewModel样式差异极大的场景,但视图和ViewModel类型耦合度稍高。
内容的提问来源于stack exchange,提问作者Elangovan Eaaswaramurty
相关产品推荐
相关产品推荐

