Reputation: 17527
I would like to return different views from a function, a Text or a VStack of a Text and a Button. Here's one of my attempts:
func buildResponseText2() -> some View {
if (userData.success) {
return VStack {
Text("Well Done")
Button("Play Again", action: {self.userData.reset()})
}
}
return VStack {
Text("Unlucky")
}
}
This does not compile, I get the error
Function declares an opaque return type, but the return statements in its body do not have matching underlying types
Is there a way to return view containers like VStack with heterogeneous contents?
Upvotes: 10
Views: 3543
Reputation: 12992
You were close to the solution, actually. Indeed, you can mark the method as ViewBuilder
and use some View
as return type this way:
@ViewBuilder
func buildResponseText2() -> some View {
if userData.success {
VStack {
Text("Well Done")
Button("Play Again", action: {self.userData.reset()})
}
} else {
Text("Unlucky")
}
}
If you prefer, you can also use a computed property the same way:
@ViewBuilder
var responseText2: some View {
if userData.success {
VStack {
Text("Well Done")
Button("Play Again", action: {self.userData.reset()})
}
} else {
Text("Unlucky")
}
}
Upvotes: 10
Reputation: 258117
Use type erasing AnyView
as return type, as below
func buildResponseText2() -> AnyView {
if (userData.success) {
return AnyView(VStack {
Text("Well Done")
Button("Play Again", action: {self.userData.reset()})
})
}
return AnyView(VStack {
Text("Unlucky")
})
}
Upvotes: 12
Reputation: 119686
You should implement a custom view with a custom view builder so you can return different types. Also, you may want to remove the VStack
from the return value, so you can decide to use a different layout later without any need to change the content.
So implement this:
struct StackContent: View {
let success: Bool
@ViewBuilder var body: some View {
switch success {
case true:
Text("Well Done")
Button("Play Again") { print("clicked") }
case false:
Text("Unlucky")
}
}
}
Then you can have this:
func buildResponseText2() -> some View {
VStak { // <- you can change it to other layout containers
StackContent(success: userData.success)
}
}
Maybe you don't need the function at all anymore ;)
Note that there is no need to write @ViewBuilder
from Swift 5.3 (Xcode 12)
Upvotes: 2