I'm trying to implement the following in SwiftUI:
struct PersonView: View {
@State private var age: Int? = 0
var body: some View {
VStack {
Text("Just a test")
if let self.age > 0 {
Text("Display Age: \(age)")
} else {
Text("Age must be greater than 0!")
}
}
}
}
But, in SwiftUI, if let
results in the following error:
Closure containing control flow statement cannot be used with function builder 'ViewBuilder'
So after researching this topic, I came across a recommendation to use .map
to unwrap the age
optional. Thus, I've modified to code within the VStack
as follows:
Text("Just a test")
self.age.map {elem in
if elem > 0 {
Text("Display Age: \(elem)")
} else {
Text("Age must be greater than 0!")
}
}
Including a conditional within the .map
closure, however, results in the following errors at the line calling the VStack
:
' (ViewBuilder.Type) -> (C0, C1) -> TupleView<(C0, C1)>' requires that '()' conform to 'View'
Type '()' does not conform to protocol 'View'
Any suggestions for how to get past the 2nd set of errors? Or, is there another approach for unwrapping optionals and evaluating them in SwiftUI? Really like SwiftUI but can't believe that unwrapping optionals has been a headache!
What SwiftUI will do is take the if statement and produce optional views that are nil or not nil depending on the conditional logic.
Closures Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages. Closures can capture and store references to any constants and variables from the context in which they’re defined.
Whether it’s a simple checkbox value or an OS availability check, there are many cases in which you want to apply different configurations to your views. A View Modifier in SwiftUI modifies the View with given configurations. An example is applying a background color to a View.
Tuesday. August 27, 2019 Looking for the tl;dr? Skip down to the Workarounds section below. In working with the SwiftUI domain specific language (DSL), you will see some familiar syntax like forEach and if <boolean condition> { }.
Now you can use conditional binding right in the view builder:
if let age = age {
if age > 0 {
Text("Display Age: \(age)")
} else {
Text("Age must be greater than 0!")
}
} else {
Text("Age not found")
}
You can refactor your code to something more basic like using a function:
var body: some View {
VStack {
Text("Just a test")
Text(text(age: age)) // Using the function
}
}
func text(age: Int?) -> String { // Defining the function
guard let age = age else { return "Age not found" }
if age > 0 { return "Display Age: \(age)" }
else { return "Age must be greater than 0!" }
}
In general, use functions where you need to cleanup your code. I hope future versions of Swift will support this directly as we expect.
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!")
}
}
}
You are trying to do two check on the value of age: first you are making sure it is not nil
and then checking that it is greater than 0.
You can use map to get rid of potential nil
and then a ternary operator to conditionally change the text displayed:
var body: some View {
VStack {
Text("Just a test")
age.map { Text( $0 > 0 ? "Display Age: \($0)" : "Age must be greater than 0!") }
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With