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