16.4_实现ViewModel之间的数据传递
掌握数据传递的核心机制
在MVVM架构中,ViewModel之间的数据传递是构建流畅用户体验的关键。 🚀 你将学习如何高效、安全地在不同ViewModel之间共享信息,确保数据一致性。这不仅提升了代码的可维护性,也让你的应用响应更加迅速。
属性注入:直接而有效
最直接的数据传递方式之一就是属性注入。当一个ViewModel需要另一个ViewModel的数据时,你可以在初始化时通过属性直接传递。
例如,假设你有一个ProductListViewModel和一个ProductDetailViewModel。当用户从列表页点击某个产品时,ProductListViewModel可以将选定产品的ID传递给ProductDetailViewModel。
class ProductDetailViewModel {
let productId: String
init(productId: String) {
self.productId = productId
}
// ... 其他逻辑
}
// 在ProductListViewModel中创建ProductDetailViewModel时
let selectedProductId = "someProductId123"
let detailViewModel = ProductDetailViewModel(productId: selectedProductId)这种方法简单明了,特别适用于父子ViewModel之间的数据传递。
闭包回调:灵活的逆向通信
有时,子ViewModel需要将数据回传给父ViewModel。这时,闭包回调就显得尤为重要。 🔄 你可以在子ViewModel中定义一个闭包属性,并在父ViewModel中设置它。
考虑一个场景:用户在SettingsViewModel中更改了某个设置,需要通知ProfileViewModel更新显示。
- 在
SettingsViewModel中定义一个闭包:swiftclass SettingsViewModel { var onSettingsChanged: ((String) -> Void)? func saveSettings(newValue: String) { // 保存设置逻辑 onSettingsChanged?(newValue) // 调用闭包通知父ViewModel } } - 在
ProfileViewModel中设置并实现闭包:swiftclass ProfileViewModel { // ... func navigateToSettings() { let settingsViewModel = SettingsViewModel() settingsViewModel.onSettingsChanged = { [weak self] updatedValue in self?.updateProfile(with: updatedValue) } // 导航到设置页面 } func updateProfile(with value: String) { print("Profile updated with: \(value)") } }
这种方式提供了极大的灵活性,允许子ViewModel在完成特定任务后通知其创建者。
观察者模式:响应式数据流
对于更复杂的数据流,尤其是当多个ViewModel需要响应同一数据源的变化时,观察者模式(如使用Combine框架)是理想选择。 🌟
- 发布者 (Publisher):一个ViewModel可以发布数据变化。
- 订阅者 (Subscriber):其他ViewModel可以订阅这些变化并做出响应。
例如,一个UserSessionViewModel可以发布用户登录状态的变化,而多个其他ViewModel(如HomeViewModel、ProfileViewModel)可以订阅这些变化来更新其UI或逻辑。
import Combine
class UserSessionViewModel: ObservableObject {
@Published var isLoggedIn: Bool = false // 发布登录状态
func login() {
isLoggedIn = true
}
func logout() {
isLoggedIn = false
}
}
class HomeViewModel: ObservableObject {
@Published var welcomeMessage: String = "请登录"
private var cancellables = Set<AnyCancellable>()
init(userSession: UserSessionViewModel) {
userSession.$isLoggedIn
.sink { [weak self] loggedIn in
self?.welcomeMessage = loggedIn ? "欢迎回来!" : "请登录"
}
.store(in: &cancellables)
}
}Combine框架提供了强大的工具来构建响应式数据流,让数据传递变得异常高效和可预测。 📈 超过70%的iOS开发者在处理复杂数据流时会选择Combine或类似的响应式框架。
代理模式:解耦与通信
代理模式(Delegate Pattern)是iOS开发中常见的通信方式,它允许一个对象(委托者)将某些职责委托给另一个对象(代理)。
- 定义协议:首先,你需要定义一个协议,其中包含代理需要实现的方法。swift
protocol ProductSelectionDelegate: AnyObject { func didSelectProduct(productId: String) } - 设置代理:在发送数据的ViewModel中,声明一个
weak var类型的代理属性。swiftclass ProductListViewModel { weak var delegate: ProductSelectionDelegate? func selectProduct(id: String) { delegate?.didSelectProduct(productId: id) } } - 实现协议:在接收数据的ViewModel中,遵循并实现该协议。swift
class HomeViewModel: ProductSelectionDelegate { // ... func navigateToProductList() { let listViewModel = ProductListViewModel() listViewModel.delegate = self // 设置代理 // 导航到产品列表页面 } func didSelectProduct(productId: String) { print("Selected product ID: \(productId)") // 根据productId进行后续操作,例如导航到详情页 } }
代理模式提供了一种清晰、解耦的通信方式,特别适合一对一的逆向通信。 🤝 这种模式在UIKit中随处可见,例如UITableViewDelegate和UICollectionViewDelegate。