'SwiftUI conditionally rendering one view or another
struct ContentView: View {
@EnvironmentObject var demoModel: DemoModel
var body: some View {
//this works
demoModel.isLoggedIn ? Text("logged in"):Text("logged out")
//this doesnt work why
//demoModel.loggedIn ? StationListView(): LoginView()
}
}
Solution 1:[1]
When you initialize a View (let's say A
) in the body of another View, what happens is that A is passed as an argument to some special functions generated by the compiler: this system of having implicit function calls in a context (in this case the body
of this View) is called "function builders", and they can be customized to have different behaviors. The one used in SwiftUI is ViewBuilder
: it "collects" all the Views that you make in the body and "merges" them in a single one (that's why the return type of body
is some View
).
ViewBuilder
contains some tricks to handle language constructs like if
statements by embedding logic like "show one view or the other" but, as of the current version of Swift (5.2), it doesn't support most other tools like 'if let, guard let, do catch'. Some of these will become available in the next Swift version.
One of the unsupported things is the ternary operator ?:
. In your example, the first line works because you're returning the same value for both the true
and the false
branches, but in the second line you're returning Views of different types, resulting in an error. Note that the same logic, used in a ViewBuilder context (Group
) works just fine:
Group {
if demoModel.isLoggedIn {
Text("Logged in")
} else {
LoginView()
}
}
And that's because ViewBuilder
knows how to manage simple if
statements.
Solution 2:[2]
For opaque returns, as some View
here, it should be returned one type, so use Group
, as below
Group {
if demoModel.loggedIn {
StationListView()
} else {
LoginView()
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Tiziano Coroneo |
Solution 2 | Asperi |