将 EnvronmentObject 传递给 NSHostingControllers

Passing an EnvronmentObject to NSHostingControllers

我正在使用自定义 SwiftUI 视图,它使用 NSSplitViewController 为两个子视图接收 ViewBuilder。我的问题是环境状态的任何变化都不会传播到 SplitView 中的子视图,而是传播到 ContentView

中的另一个 TextView
import SwiftUI

class AppEnvironment : ObservableObject {
    @Published var value: String = "default"
}

struct ContentView: View {
    @EnvironmentObject var env : AppEnvironment

    var body: some View {
        HStack {
            Button(action: {
                self.env.value = "new value"
            }, label: { Text("Change value") })
            Text(self.env.value)
                GeometryReader { geometry in
                    SplitView(master: {
                        Text("master")
                            .background(Color.yellow)
                    }, detail: {
                        HStack {
                            Text(self.env.value)                        }
                            .background(Color.orange)
                    }).frame(width: geometry.size.width, height: geometry.size.height)
                }
        }
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

//
// Source: https://gist.github.com/HashNuke/f8895192fff1f275e66c30340f304d80
//
struct SplitView<Master: View, Detail: View>: View {
    var master: Master
    var detail: Detail

    init(@ViewBuilder master: () -> Master, @ViewBuilder detail: () -> Detail) {
        self.master = master()
        self.detail = detail()
    }

    var body: some View {
        let viewControllers = [NSHostingController(rootView: master), NSHostingController(rootView: detail)]
        return SplitViewController(viewControllers: viewControllers)
    }
}

struct SplitViewController: NSViewControllerRepresentable {
    var viewControllers: [NSViewController]

    private let splitViewResorationIdentifier = "com.company.restorationId:mainSplitViewController"

    func makeNSViewController(context: Context) -> NSViewController {
        let controller = NSSplitViewController()

        controller.splitView.dividerStyle = .thin
        controller.splitView.autosaveName = NSSplitView.AutosaveName(splitViewResorationIdentifier)
        controller.splitView.identifier = NSUserInterfaceItemIdentifier(rawValue: splitViewResorationIdentifier)
        let vcLeft = viewControllers[0]
        let vcRight = viewControllers[1]
        vcLeft.view.widthAnchor.constraint(greaterThanOrEqualToConstant: 30).isActive = true
        vcRight.view.widthAnchor.constraint(greaterThanOrEqualToConstant: 70).isActive = true
        let sidebarItem = NSSplitViewItem(contentListWithViewController: vcLeft)
        sidebarItem.canCollapse = false

        // I'm not sure if this has any impact
        // controller.view.frame = CGRect(origin: .zero, size: CGSize(width: 800, height: 800))
        controller.addSplitViewItem(sidebarItem)

        let mainItem = NSSplitViewItem(viewController: vcRight)
        controller.addSplitViewItem(mainItem)

        return controller
    }

    func updateNSViewController(_ nsViewController: NSViewController, context: Context) {
        print("should update splitView")
    }
}

是的,在这种情况下,不会自动注入 EnvironmentObject。解决方案是将内容分离到指定的视图(为了更好的设计)并手动注入环境对象。

在这里

Text(self.env.value)
    GeometryReader { geometry in
        SplitView(master: {
            MasterView().environmentObject(self.env)
        }, detail: {
            HStack {
                DetailView().environmentObject(self.env)
        }).frame(width: geometry.size.width, height: geometry.size.height)
    }

和观看次数

struct MasterView: View {
    var body: some View {
        Text("master")
            .background(Color.yellow)
    }
}

struct DetailView: View {
    var body: some View {
        HStack {
            Text(self.env.value)                        }
            .background(Color.orange)
    }
}