How Do I Create an NSHostingView?

A bit of background first:


I have a document based app that's a file viewer. Each file contains a bunch of tables. Each document window is a horizontal NSStackView. The left hand view shows a list of tables in the file and the right hand view is an NSTabView with two tabs. One to show the unformatted version of the table (i.e. a hex dump) and the other to show a formatted view. Currently the formatted view is a scrollable NSTableView with a single table cell.


And my (probably stupid) questions:

I want to use a different view for each table type, but I couldn't figure out a way to do that with AppKit. When I learned about SwiftUI, I though it would be easy to create a view that changed based on the table type, and I could put this view into an NSHostingView. I have two questions:


1) I thought there'd be a way to create an NSHostingView in the storyboard, just like there is for an NSHostingViewController, but I don't see any way to to that. Have I missed somthing? Is this an as yet unimplemented feature, or is there another way to do it.


2) I tried to make a SwiftUIView that contains a different view depending on the type of the selected table, but I can't figure out how to do this. Here's what I tried:


import SwiftUI


@available(OSX 10.15.0, *)

struct FormattedView: View {


var table: Table


var body: some View {

formattedView(table.type)

}


func formattedView(_ tableType: TableType) -> some View {

List {

if tableType == TableType.oneType {

OneTypeTableFormattedView(table)

} else if tableType == TableType.anotherType {

AnotherTypeTableFormattedView(table)

} else {

Text("No formatted view for \(tableType) table")

}

}

}

}


The formattedView function doesn't compile unless I embed the body in a List. It also dosen't compile if I use a switch statement instead of a if-else chain. I guess this means I'm not on the right track here. Is there a better way to do this?

Replies

I was able to answer the first question by generating an empty document based App and looking at how it is set up.


I read about opague types and "some" in the Swift 5.1 manual and thought that I could solve this problem by ovinb the Text() View into another View. Both views still have differnt types, and so it still doesn't compile...

Compiler gives error because it expects to return opaque (only one) type of view.

You may want to use AnyView. It will return the same view which you have created. But it might be costly operation. So you can wrap every thing under Group. This will be best option for you. But You can choose either way to come out of compile error.

Option 1.

func formattedView(_ tableType: TableType) -> some View {

List {

if tableType == TableType.oneType {

return AnyView(OneTypeTableFormattedView(table))

} else if tableType == TableType.anotherType {

return AnyView(AnotherTypeTableFormattedView(table))

} else {

return AnyView(Text("No formatted view for \(tableType) table"))

}

}

}

}


Option 2.

func formattedView(_ tableType: TableType) -> some View {

Group {

List {

if tableType == TableType.oneType {

return OneTypeTableFormattedView(table)

} else if tableType == TableType.anotherType {

return AnotherTypeTableFormattedView(table)

} else {

return Text("No formatted view for \(tableType) table")

}

}

}

}

}