Conditionally Use View in Swiftui

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



Leave a reply



Submit