13.3_当ViewModel数据变化时更新UI组件
在 MVVM 架构中,当 ViewModel 的数据发生变化时,如何高效且优雅地更新 UI 组件是至关重要的。让我们一起探索如何在 iOS 18 中,利用 UIKit 和纯代码实现这一目标!🚀
观察者模式与数据绑定
首先,你需要理解观察者模式。ViewModel 充当被观察者,而 ViewController 中的 UI 组件则是观察者。当 ViewModel 中的数据改变时,它会通知所有订阅的观察者,从而触发 UI 更新。数据绑定是实现这一过程的关键。
- KVO (Key-Value Observing):这是 Objective-C 时代的经典方法,但在 Swift 中使用起来稍显繁琐。
- Combine: 这是苹果官方推出的响应式编程框架,非常适合处理异步事件和数据流。
- 闭包 (Closures):你可以使用闭包作为回调函数,在 ViewModel 数据变化时执行 UI 更新。
使用 Combine 实现 UI 更新
Combine 框架提供了强大的数据流处理能力。你可以使用 Published 属性包装 ViewModel 中的数据,使其成为可观察的属性。在 ViewController 中,使用 sink 方法订阅这些属性的变化,并更新 UI。
swift
class MyViewModel {
@Published var title: String = "初始标题"
}
class MyViewController: UIViewController {
let viewModel = MyViewModel()
var titleLabel: UILabel!
private var cancellables: Set<AnyCancellable> = []
override func viewDidLoad() {
super.viewDidLoad()
viewModel.$title
.sink { [weak self] newTitle in
self?.titleLabel.text = newTitle
}
.store(in: &cancellables)
}
}使用闭包进行简单的数据绑定
如果你不想引入 Combine 框架,可以使用闭包来实现简单的数据绑定。在 ViewModel 中定义一个闭包属性,并在数据变化时调用它。在 ViewController 中,将 UI 更新代码赋值给这个闭包。
swift
class MyViewModel {
var title: String = "初始标题" {
didSet {
titleDidChange?(title)
}
}
var titleDidChange: ((String) -> Void)?
}
class MyViewController: UIViewController {
let viewModel = MyViewModel()
var titleLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
viewModel.titleDidChange = { [weak self] newTitle in
self?.titleLabel.text = newTitle
}
}
}线程安全与 UI 更新
UI 更新必须在主线程上进行。当 ViewModel 在后台线程更新数据时,你需要确保 UI 更新代码在主线程上执行。你可以使用 DispatchQueue.main.async 来实现这一点。
swift
viewModel.$title
.sink { [weak self] newTitle in
DispatchQueue.main.async {
self?.titleLabel.text = newTitle
}
}
.store(in: &cancellables)优化 UI 更新性能
频繁的 UI 更新可能会影响性能。你可以使用以下技巧来优化 UI 更新:
- Diff 算法: 只更新需要改变的部分,而不是整个 UI 组件。
- 节流 (Throttling):限制 UI 更新的频率。
- 延迟更新: 在短时间内合并多次数据变化,然后一次性更新 UI。
通过以上方法,你可以确保 ViewModel 的数据变化能够及时、高效地反映在 UI 上,从而提升用户体验。🎉