I'm trying to provide a swiftUI view with a reference to its parent NSHostingController
This allows (for example) the close button to call parent.dismiss()
I'm trying to do this by creating a subclass of NSHostingController which provides itself as an environment variable.
I'm getting tied up in knots though trying to specify the generics
Is there a way to achieve something like the following?
class HSHostingController<Content,Modified>:NSHostingController<Modified> where Content : View, Modified : View {
init(rootView:Content) {
let container = Container()
let modified:Modified = rootView.environmentObject(container)
super.init(rootView: modified)
container.controller = self
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
where
class Container:ObservableObject {
weak var controller:NSViewController?
}
the error as written is that it Cannot convert value of type 'some View' to expected argument type 'Modified'
I think I need to specify the type of the parent more explicitly - but can't figure how to do that.
the modified root view is actually of type:
SwiftUI.ModifiedContent<Content, SwiftUI._EnvironmentKeyWritingModifier<Container?>>
Any help appreciated.
thank you
ok - answering my own question.
this works
class Container:ObservableObject {
weak var controller:NSViewController?
}
class HSHostingController<Content>:NSHostingController<ModifiedContent<Content,SwiftUI._EnvironmentKeyWritingModifier<Container?>>> where Content : View {
init(rootView:Content) {
let container = Container()
let modified = rootView.environmentObject(container) as! ModifiedContent<Content, _EnvironmentKeyWritingModifier<Container?>>
super.init(rootView: modified)
container.controller = self
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
how problematic is it to be using _EnvironmentKeyWritingModifier
mattcurtis
(Matt Curtis)
3
I think the easiest option would be to use AnyView and define your type as a subclass of NSHostingController<AnyView>, but if for whatever reason you want to avoid AnyView here's another option:
class Container: ObservableObject {
weak var controller: NSViewController?
}
protocol RootViewModifyingHostingController {
associatedtype ModifiedRootView: View
associatedtype RootView: View
@ViewBuilder static func modify(rootView: RootView, container: Container) -> ModifiedRootView
}
class MyHostingController<RootView: View>: NSHostingController<MyHostingController.ModifiedRootView>, RootViewModifyingHostingController {
init(rootView: RootView) {
let container = Container()
let modified = Self.modify(rootView: rootView, container: container)
super.init(rootView: modified)
container.controller = self
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
static func modify(rootView: RootView, container: Container) -> some View {
rootView
.environmentObject(container)
}
}
1 Like
I hadn't thought of just going for AnyView!
That's a neat trick using associated types to get the 'some View' indirectly into the constraint.
Thank you
1 Like
cyrilzakka
(Cyril Zakka, MD)
5
Would you mind giving me an example of how to use this?
mattcurtis
(Matt Curtis)
6
In the snippet you're referring to you can really just use it nearly as-is. You would create an instance of the hosting controller, MyHostingController(rootView: Text('hello')) etc.
I can be more helpful if you can say what you're trying to do and what issue you're running into.