Skip to content

16.4_实现ViewModel之间的数据传递

掌握数据传递的核心机制

在MVVM架构中,ViewModel之间的数据传递是构建流畅用户体验的关键。 🚀 你将学习如何高效、安全地在不同ViewModel之间共享信息,确保数据一致性。这不仅提升了代码的可维护性,也让你的应用响应更加迅速。

属性注入:直接而有效

最直接的数据传递方式之一就是属性注入。当一个ViewModel需要另一个ViewModel的数据时,你可以在初始化时通过属性直接传递。

例如,假设你有一个ProductListViewModel和一个ProductDetailViewModel。当用户从列表页点击某个产品时,ProductListViewModel可以将选定产品的ID传递给ProductDetailViewModel

swift
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中定义一个闭包:
    swift
    class SettingsViewModel {
        var onSettingsChanged: ((String) -> Void)?
    
        func saveSettings(newValue: String) {
            // 保存设置逻辑
            onSettingsChanged?(newValue) // 调用闭包通知父ViewModel
        }
    }
  • ProfileViewModel中设置并实现闭包:
    swift
    class 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(如HomeViewModelProfileViewModel)可以订阅这些变化来更新其UI或逻辑。

swift
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开发中常见的通信方式,它允许一个对象(委托者)将某些职责委托给另一个对象(代理)。

  1. 定义协议:首先,你需要定义一个协议,其中包含代理需要实现的方法。
    swift
    protocol ProductSelectionDelegate: AnyObject {
        func didSelectProduct(productId: String)
    }
  2. 设置代理:在发送数据的ViewModel中,声明一个weak var类型的代理属性。
    swift
    class ProductListViewModel {
        weak var delegate: ProductSelectionDelegate?
    
        func selectProduct(id: String) {
            delegate?.didSelectProduct(productId: id)
        }
    }
  3. 实现协议:在接收数据的ViewModel中,遵循并实现该协议。
    swift
    class HomeViewModel: ProductSelectionDelegate {
        // ...
        func navigateToProductList() {
            let listViewModel = ProductListViewModel()
            listViewModel.delegate = self // 设置代理
            // 导航到产品列表页面
        }
    
        func didSelectProduct(productId: String) {
            print("Selected product ID: \(productId)")
            // 根据productId进行后续操作,例如导航到详情页
        }
    }

代理模式提供了一种清晰、解耦的通信方式,特别适合一对一的逆向通信。 🤝 这种模式在UIKit中随处可见,例如UITableViewDelegateUICollectionViewDelegate

本站使用 VitePress 制作