SwiftUI: использовать разные объекты среды для каждого представления - PullRequest
0 голосов
/ 28 февраля 2020

В SwiftUI у меня есть пара разных экранов. Для каждого экрана у меня есть ViewController, SwiftUI View и комбинированный EnvironmentObject. Объединенный EnvironmentObject имеет два наблюдаемых класса, один - ViewModel, а другой - Interactor.

Как бы я использовал Interactor и ViewModel для каждого представления SwiftUI без использования ViewController, как я? И где есть разные 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)
                })
            }
    }
}

1 Ответ

0 голосов
/ 28 февраля 2020

Объекты среды внедряются по типам, поэтому вы можете внедрить отдельно представление модели и интегратор, как показано ниже

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>

и, наконец, и требуется, для root вида оба должны быть введены в конструкцию, как (поцарапано)

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

порядок не имеет значения, так как введен по типу.

Протестировано с Xcode 11.2 / iOS 13.2

...