Most SwiftUI view modifier functions take nil
as a parameter that ‘turns off’ the modifier. For example, padding(_:_:)
can take nil
as the first argument, which disables the padding modifier. This is distinct to disabling all the padding with padding(0)
.
if
https://developer.apple.com/documentation/swiftui/view/padding(_:_:)nil
the specified or system-calculated mount is applied to all edges.
However, this isn't always possible. For example, id(_:)
takes nil as a distinct ID separate to not calling the view modifier. Another example is using if #available(iOS 15, *) {…}
to call different view modifiers — this can't be done inline.
if
Add the following in an extension View {…}
:
/// Closure given view if conditional.
/// - Parameters:
/// - conditional: Boolean condition.
/// - content: Closure to run on view.
@ViewBuilder func `if`<Content: View>(_ conditional: Bool, @ViewBuilder _ content: (Self) -> Content) -> some View {
if conditional {
content(self)
} else {
self
}
}
Code language: Swift (swift)
This adds an if
view modifier which can contain any code you desire, including if #available
checks.
Pass a conditional as the first argument, then a ViewBuilder as a second argument. If the conditional is true, the view modifiers in the closure will be applied. Apply your modifiers to $0
in the closure.
Text("foo")
.if(x == 2) {
$0.bold()
}
Code language: JavaScript (javascript)
The default for the first argument is true
, so you can omit this if you want to add your own code for switching view modifiers. For example, an if #available
check:
List { … }
.if {
if #available(iOS 15, *) {
$0.refreshable { … }
} else {
$0
}
}
Code language: PHP (php)
if else
A modification to the original if
function lets you pass an else
ViewBuilder too.
/// Closure given view if conditional.
/// - Parameters:
/// - conditional: Boolean condition.
/// - truthy: Closure to run on view if true.
/// - falsy: Closure to run on view if false.
@ViewBuilder func `if`<Truthy: View, Falsy: View>(
_ conditional: Bool = true,
@ViewBuilder _ truthy: (Self) -> Truthy,
@ViewBuilder else falsy: (Self) -> Falsy
) -> some View {
if conditional {
truthy(self)
} else {
falsy(self)
}
}
Code language: Swift (swift)
Call this with another trailing closure for the false case of the conditional.
Text("georgegarside.com")
.if(x == 73) {
$0.bold()
} else: {
$0.italic()
}
Code language: JavaScript (javascript)
if let
Similarly, you can reproduce if let
inside view modifiers too with a minor adaptation of the previous extension. If the optional is none, the closure is not executed, otherwise the closure is executed with the unwrapped optional passed as the second argument.
/// Closure given view and unwrapped optional value if optional is set.
/// - Parameters:
/// - conditional: Optional value.
/// - content: Closure to run on view with unwrapped optional.
@ViewBuilder func iflet<Content: View, T>(_ conditional: Optional<T>, @ViewBuilder _ content: (Self, _ value: T) -> Content) -> some View {
if let value = conditional {
content(self, value)
} else {
self
}
}
Code language: Swift (swift)
Access the view with $0
and the unwrapped optional value with $1
.
Text("@grgarside on Twitter")
.iflet(socialFont) {
$0.font($1)
}
Code language: Swift (swift)