Reputation: 3163
I’m building like a demo app of different examples, and I’d like the root view to be a List
that can navigate to the different example views. Therefore, I tried creating a generic Example
struct which can take different destinations View
s, like this:
struct Example<Destination: View> {
let id: UUID
let title: String
let destination: Destination
init(title: String, destination: Destination) {
self.id = UUID()
self.title = title
self.destination = destination
}
}
struct Example1View: View {
var body: some View {
Text("Example 1!")
}
}
struct Example2View: View {
var body: some View {
Text("Example 2!")
}
}
struct ContentView: View {
let examples = [
Example(title: "Example 1", destination: Example1View()),
Example(title: "Example 2", destination: Example2View())
]
var body: some View {
List(examples, id: \.id) { example in
NavigationLink(destination: example.destination) {
Text(example.title)
}
}
}
}
Unfortunately, this results in an error because examples
is a heterogeneous collection:
I totally understand why this is broken; I’m creating a heterogeneous array of examples because each Example
struct has its own different, strongly typed destination. But I don’t know how to achieve what I want, which is an array that I can make a List
out of which has a number of different allowed destinations.
I’ve run into this kind of thing in the past, and in the past I’ve gotten around it by wrapping my generic type and only exposing the exact properties I needed (e.g. if I had a generic type that had a title, I would make a wrapper struct and protocol that exposed only the title, and then made an array of that wrapper struct). But in this case NavigationLink
needs to have the generic type itself, so there’s not a property I can just expose to it in a non-generic way.
Upvotes: 3
Views: 1218
Reputation: 8096
You can use the type-erased wrapper AnyView
. Instead of making Example
generic, make the destination view inside of it be of type AnyView
and wrap your views in AnyView
when constructing an Example
.
For example:
struct Example {
let id: UUID
let title: String
let destination: AnyView
init(title: String, destination: AnyView) {
self.id = UUID()
self.title = title
self.destination = destination
}
}
struct Example1View: View {
var body: some View {
Text("Example 1!")
}
}
struct Example2View: View {
var body: some View {
Text("Example 2!")
}
}
struct ContentView: View {
let examples = [
Example(title: "Example 1", destination: AnyView(Example1View())),
Example(title: "Example 2", destination: AnyView(Example2View()))
]
var body: some View {
NavigationView {
List(examples, id: \.id) { example in
NavigationLink(destination: example.destination) {
Text(example.title)
}
}
}
}
}
Upvotes: 7