首页 > 解决方案 > 对两个独立但半关联的视图模型的视图之间的特定状态变化进行通信/做出反应

问题描述

我有一个由两个子视图组成的视图,其中 1) 独立的内部状态,但 2) 一个对任何关联视图(即彼此)都值得注意的高级状态。也就是说,一个人的高级状态的变化应该触发另一个人重新考虑/改变自己的。想象一下——为了保持简单/通用——一个标题,它有一组按钮,这些按钮根据页脚中选择的选项而改变。另一方面,页眉中有一个按钮退出活动状态并将其重置回其原始状态(因此页脚也是如此)。

我设法通过为每个视图的视图模型采用共享通用实例来方便地实现这一点。例如,页眉观察页脚视图模型的变化,因此能够对页脚高级状态的变化做出反应。然而,考虑到页脚有其他内部状态(即与页眉无关,但对于观察其自己的视图模型变化的页脚视图是必需的),每次有任何变化时都会重新渲染页眉视图,无论相关性如何, 到页脚视图。实际上,至少在我看来,我不会注意到任何视觉问题,但我知道这是管理状态流的根本低效(和错误)方式。如果这种不当行为很普遍,那么性能很可能会受到影响(可见)。

我所拥有的大致如下:

struct HeaderView: View {
        
    // MARK: Observers
    @ObservedObject var viewModel: HeaderViewModel = HeaderViewModel.shared
    @ObservedObject var footerViewModel: FooterViewModel = FooterViewModel.shared
    
    // MARK: -
    var body: some View {
        VStack {
            switch footerViewModel.state {
                case 1: HeaderState1View()
                case 2: HeaderState2View()
                case 3: HeaderState3View()
            }
            Button("EXIT", action: {viewModel.reset()})
        }
    }
}

struct FooterView: View {
        
    // MARK: Observers
    @ObservedObject var viewModel: FooterViewModel = FooterViewModel.shared
    
    // MARK: -
    var body: some View {
        Button("State 1", action: {viewModel.state(to: 1)})
            .background(viewModel.state == 1 ? Color.red : Color.clear)
        Button("State 2", action: {viewModel.reset()})
            .background(viewModel.state == 2 ? Color.red : Color.clear)
        Button("State 3", action: {viewModel.reset()})
            .background(viewModel.state == 3 ? Color.red : Color.clear)
    }
}

class HeaderViewModel: ObservableObject {
    // ...
    
    func reset() {
        FooterViewModel.shared.reset()
        // and other internal state changes
    }

    // ...
}

class FooterViewModel: ObservableObject {
    
    @Published var state: Int = 1
    // ...
    
    func state(to state: Int) {
        self.state = state
    }

    func reset() {
        self.state = 1
    }

    // ...
}

每个视图模型都是在负责创建两者(和其他)的根级视图模型中创建的。

class ViewModel {
    var footer = FooterViewModel()
    var header = HeaderViewModel()
    // etc.
}

这是在 @main 根结构中创建的:

@main
struct myApp: App {
    
    // MARK: Variables
    var viewModel = ViewModel()
    
    // MARK: Layout
    var body: some Scene {        
        return WindowGroup {
            ContentView()
                .environmentObject(viewModel)
        }
    }
}

struct ContentView: View {
    
    // MARK: -
    var body: some View {
        VStack {
            header
            /// other views
            footer
        }
    }

如上所述,不依赖页眉和页脚视图模型的共享实例,实现我所追求的最合适/最有效的方法是什么?

更具体/更清楚的是,例如,如果 Header 和 Footer 视图创建了它们各自视图模型的 @StateObject ,即

@StateObject var viewModel: FooterViewModel = FooterViewModel() /// in FooterView.swift
@StateObject var viewModel: HeaderViewModel = HeaderViewModel() /// in HeaderView.swift

我怎样才能让 HeaderView 中的任何更改与 FooterView 创建的 FooterViewModel 实例通信?

编辑:

我想可能实现这一点的一种方法是,如果我创建包含已发布共享状态的第三个视图模型,并且在初始化时将其传递到两个视图中。但是想象一下,需要在视图之间共享多个已发布属性,并且在混合中还有另一个视图只需要访问其中一个——那么我是否会创建两个共享视图模型:一个与发布者共享所有 3 和另一个与 2 之间的属性?似乎不是一个实用的策略。

标签: swiftmvvmviewmodelpublish-subscribecombine

解决方案


推荐阅读