Conditionally use view in SwiftUI
The simplest way to avoid using an extra container like HStack
is to annotate your body
property as @ViewBuilder
, like this:
@ViewBuilder
var body: some View {
if user.isLoggedIn {
MainView()
} else {
LoginView()
}
}
SwiftUI conditionally rendering one view or another
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.
Conditionally apply overlay in SwiftUI
Almost all modifiers accept a nil
value for no change.
So basically you can write
.overlay(views > 1 ? Button(action: { ... }, label: { ... }) : nil)
It becomes more legible if you extract the button to an extra view struct.
SwiftUI - alternative to if let with a conditional closure
For such cases I prefer the following approach
struct PersonView: View {
@State private var age: Int? = 0
var body: some View {
VStack {
Text("Just a test")
AgeText
}
}
private var AgeText: some View {
if let age = self.age, age > 0 {
return Text("Display Age: \(age)")
} else {
return Text("Age must be greater than 0!")
}
}
}
How to make a SwiftUI conditional modifier works with different result types?
Or if you want to definitely stick with ternary:
Text("Hello, world!")
.padding()
.background(
ZStack {
RoundedRectangle(cornerRadius: 6)
.fill(.regularMaterial)
.opacity(condition ? 1 :0)
Circle()
.fill(.regularMaterial)
.opacity(condition ? 0 :1)
}
)
Changing views via conditional statements inside of async commands in SwiftUI IOS15
SwiftUI renders your user interface in response to changes in state. So instead of trying to render Dashboard
or ContentView
within your Task
, instead you need to set some form of state higher up that determines which view to show to the user.
For example, if you had a @State
variable recording your logged in status, your code would start to look like
struct ContentView: View {
@State var loggedIn = false
var body: some View {
if loggedIn {
Dashboard()
} else {
// login form
Button {
Task {
let logInStatus = try await network.LoginFunction(userName: username, passWord: password)
self.loggedIn = logInStatus
}
}
}
}
}
When your async
tasks changes the ContentView's state, SwiftUI's rendering subsystem will pick up that loggedIn
has changed, and re-render body
so that the login form is replaced by a call to Dashboard()
.
How to treat if-let-else as a single view in SwiftUI?
Make it closure argument a view builder, like
extension View {
func statusBar(@ViewBuilder statusBar: () -> V) -> some View {
self.modifier(StatusBarView(statusBar: statusBar))
}
}
the same can be done in init of modifier, but not required specifically for this case of usage.
Tested with Xcode 13.4 / iOS 15.5
Related Topics
Implicit Return in a Closure Causing an Error
Segue from a Slpagingviewswift Vc and Dismiss the Destination Vc to Return
Ondelete Causing Nsrangeexception
Compare App Versions After Update Using Decimals Like 2.5.2
What Are 'Get' and 'Set' in Swift
Create Codable Struct with Generic Type
How to Validate Dynamically Added Textfields on a Button Click in Swiftui
How to Use Keywords as Parameter Names in Swift
Coreplot with Swift: There Is No Yaxis.Majorintervallength
How to Retrieve a Random Object from Firebase Using a Sequential Id
Draw a Hole in a Rectangle with Spritekit
iOS Facebook Sdk: Login Doesn't Return Email Despite Permissions Granted
Swift 4.2 Imagepickercontroller Issue
Implementing a Function with a Default Parameter Defined in a Protocol