Skip to content

Instantly share code, notes, and snippets.

@ole
Created May 5, 2023 18:21
Show Gist options
  • Save ole/430385f11d0ffc3a16659198746e118f to your computer and use it in GitHub Desktop.
Save ole/430385f11d0ffc3a16659198746e118f to your computer and use it in GitHub Desktop.
Avoiding AnyView when using NSHostingView with arbitrary content. Finding a way to specify the generic parameter.
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
FrameworkView1 {
Text("FrameworkView1")
}
.border(.red)
FrameworkView2 {
Text("FrameworkView2")
}
.border(.red)
FrameworkView3 {
Text("FrameworkView3")
}
.border(.red)
}
.padding()
}
}
struct FrameworkView1<Content: View>: NSViewRepresentable {
@ViewBuilder var content: Content
// Problem: we want to add modifiers to the `content` view.
// Adding modifiers changes the type of the resulting SwiftUI view.
// We can use AnyView as the content type, but that's ugly and can make
// SwiftUI view updates slow and/or break animations.
typealias NSViewType = NSHostingView<AnyView>
func makeNSView(context: Context) -> NSViewType {
let hostingViewContent = content
.padding()
.background(.green)
return NSHostingView(rootView: AnyView(hostingViewContent))
}
func updateNSView(_ nsView: NSViewType, context: Context) {
}
}
// MARK: Solution 1
// Solution 1: define a protocol, then we can use `some NSHostingViewProtocol`
// as the return type.
protocol NSHostingViewProtocol: NSView {}
extension NSHostingView: NSHostingViewProtocol {}
struct FrameworkView2<Content: View>: NSViewRepresentable {
@ViewBuilder var content: Content
func makeNSView(context: Context) -> some NSHostingViewProtocol {
let hostingViewContent = content
.padding()
.background(.green)
print(type(of: hostingViewContent))
return NSHostingView(rootView: hostingViewContent)
}
func updateNSView(_ nsView: NSViewType, context: Context) {
}
}
// MARK: Solution 2
// Solution 2: define a wrapper view struct.
// This hides the concrete view type behind the wrapper view's `some View`.
struct FrameworkView3<Content: View>: NSViewRepresentable {
@ViewBuilder var content: Content
typealias NSViewType = NSHostingView<RootView>
func makeNSView(context: Context) -> NSViewType {
return NSHostingView(rootView: RootView(content: content))
}
func updateNSView(_ nsView: NSViewType, context: Context) {
}
struct RootView: View {
var content: Content
var body: some View {
content
.padding()
.background(.green)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment