You can have if statements inside of a Group's view builder. I made an example to illustrate:
var label1 = "label 1"
@State var label2: String?
var label3: String? = "label 3"
var body: some View {
VStack {
Text(label1)
Group {
if label2 != nil {
Text(label2!)
}
if label3 != nil {
Text(label3!)
}
}
Button(action: {
self.label2 = "Button pressed!"
}) {
Text("Press me!")
}
}
}
Of course, in this example, you could just do Text(label2 ?? ""), but there would be a gap where the Text is until label2 has a value, and also, in more complex situations, this allows for more flexibility. If you run that code, you will see "Label 1" directly above "Label 3", and if you press the button, "Button pressed!" will appear in between "Label 1" and "Label 3".
Basically, if label2 is nil, there won't be a blank space where the label will be when it has a value, instead there will be no gap, and when label2 is assigned a value, everything above and below where it should be will shift to accommodate for it.
In the code you provided, you can change it to:
HStack {
Group {
if profile.height != nil {
TagBox(field: "height", value: String(profile.height!))
}
}
TagBox(field: "nationality", value: profile.nationality)
Spacer()
}.padding(.horizontal)
The nice thing about this solution is that you can do more complex things in the scenario that the value is nil (or not), and even provide a default view in case the value is nil (In my example, this would be the equivalent of adding an else
for either if statement with a Button, Text, etc. inside of the else's body).
One drawback is that you have to explicitly unwrap the optionals, which shouldn't be a problem because the if statement takes care of making sure it is not nil, but I still find that optional binding is generally a cleaner route for unwrapping optionals.
Edit:
If you really don't want to explicitly unwrap optionals, you can use ViewBuilders:
var label1 = "label 1"
@State var label2: String?
var label3: String? = "label 3"
var body: some View {
VStack {
Text(label1)
Group {
self.conditionalText(self.label2)
if label3 != nil {
Text(label3!)
}
}
Button(action: {
self.label2 = "Button pressed!"
}) {
Text("Press me!")
}
}
}
func conditionalText(_ text: String?) -> some View {
if let text = text {
return ViewBuilder.buildIf(Text(text))
} else {
print("Text is nil")
}
let view: Text? = nil
return ViewBuilder.buildIf(view)
}
This method also highlights how you can have executable code inside of a Group as long as it is inside of a function that returns a view.