【问题标题】:SwiftUI: use a different environment object for each viewSwiftUI:为每个视图使用不同的环境对象
【发布时间】:2020-02-28 11:07:44
【问题描述】:

在 SwiftUI 中,我有几个不同的屏幕。对于每个屏幕,我都有一个 ViewController、SwiftUI View 和一个组合的 EnvironmentObject。组合后的 EnvironmentObject 有两个 observable 类,一个是 ViewModel,另一个是 Interactor。

如何在不使用 ViewController 的情况下为每个 SwiftUI 视图使用 Interactor 和 ViewModel?并且每个 View 都有不同的 EnvironmentObject。

import SwiftUI
import Combine

class ViewModel1: ObservableObject {
    //Published data for view
    @Published var text: String

    init(text: String) {
        self.text = text
    }
}


class Interactor1<VC: UIViewController>: ObservableObject {

    let vc: VC
    let viewModel: ViewModel1?

    init(vc: VC, viewModel: ViewModel1?) {
        self.vc = vc
        self.viewModel = viewModel
    }

    func buttonClicked() {
        //present second ViewController by using vc.present...
    }
}


class CombinedObject<VC: UIViewController>: ObservableObject {
    @Published var interactor: Interactor1<VC>
    @Published var viewModel: ViewModel1

    var anyCancellable: AnyCancellable? = nil
    var anyCancellable2: AnyCancellable? = nil

    init(vc: VC, index: Int) {
        viewModel = ViewModel1(text: "text1")

        interactor = Interactor1(vc: vc, viewModel: nil)
        interactor = Interactor1(vc: vc, viewModel: viewModel)

        anyCancellable = interactor.objectWillChange.sink { (_) in
            self.objectWillChange.send()
        }

        anyCancellable2 = viewModel.objectWillChange.sink { (_) in
            self.objectWillChange.send()
        }
    }
}


class ViewController1: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let combinedObject = CombinedObject(vc: self, index: 0)
        let view1 = View1<ViewController1>().environmentObject(combinedObject)
        let hostingController = UIHostingController(rootView: view1)

        self.addChild(hostingController)
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(hostingController.view)
        hostingController.didMove(toParent: self)
    }
}


struct View1<VC1: UIViewController>: View {
    @EnvironmentObject var combinedObject: CombinedObject<VC1>

    var body: some View {
        VStack() {
            Button(action: {
                self.combinedObject.interactor.buttonClicked()
            }, label: {
                Text(self.combinedObject.viewModel.text)
                })
            }
    }
}

【问题讨论】:

    标签: swiftui swift5


    【解决方案1】:

    环境对象是按类型注入的,因此您可以分别注入视图模型和交互器,如下所示

    struct View1<VC1: UIViewController>: View {
        @EnvironmentObject var viewModel: ViewModel1
        @EnvironmentObject var iterator: Interactor1<VC1>
    
        ...
    

    它们将可用于所有子视图(两者,但独立,基于声明),例如

    struct SubView1<VC1: UIViewController>: View { // only with view model
        @EnvironmentObject var viewModel: ViewModel1
    
    struct SubView2<VC1: UIViewController>: View { // only with iterator
        @EnvironmentObject var iterator: Interactor1<VC1>
    

    最后也是必需的,对于根视图,两者都必须在构造时注入,例如(scratchy)

    View1(...)
       .environmentObject(ViewModel1(...))
       .environmentObject(Interactor1(...))
    

    顺序无关紧要,按类型注入。

    使用 Xcode 11.2 / iOS 13.2 测试

    【讨论】:

    • 这意味着我会在显示根视图之前加载所有屏幕的数据。也许不可能做我想要实现的目标,但这种方式至少不能很好地工作,因为加载大量甚至可能不需要的数据是昂贵且不必要的(在用户的情况下)谁不会去所有的意见)。可能视图可以在需要时调用一些方法来设置环境数据?无论如何,我对如何在不使用 vc 部分的情况下很好地设置架构有点困惑。
    • @DevB2F,等等……你在组合对象中没有做同样的事情吗?我以为你只是想单独这样做......而且,这只是一种设置环境的机制,你不必一次加载所有内容,有些可以是惰性的,有些可以最近注入,有些可以计算,等等. 这只是一种设置层次结构中任何地方可用的引用的可能性。其他一切都在您的控制之下。
    猜你喜欢
    • 2021-03-26
    • 1970-01-01
    • 1970-01-01
    • 2019-10-23
    • 1970-01-01
    • 2020-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多