How to interpret VStack{ .. } from SwiftUI's official tutorial code?
If you look at the docs for VStack.init
, you'll see that the last argument it accepts is indeed a closure. The magic here is that the closure is marked with @ViewBuilder
.
@ViewBuilder
is a kind of function builder. The idea is that you pass in a closure containing a bunch of expressions, and then the function builder will combine those expressions into a single value. It's kind of like returning an array, but in an arguably better-looking syntax. (it's not really an array though. The return type of the closure is decided by the function builder.)
In your code, you are returning an "array" of 4 views.
MapView
CircleImage
- another
VStack
Spacer
These will get passed to the ViewBuilder
, and it combines all of them into a single View
object.
And if you are wondering what the methods called on at the end of each view are doing, they are just methods that return slight modifications of the objects on which they are called. For example padding
returns the same view, but with some padding applied.
When you look at the signature of VStack
initializer:
public init(alignment: HorizontalAlignment = .center, spacing: CGFloat? = nil, @ViewBuilder content: () -> Content)
You can see that it takes 3 arguments, the first two have default values so they can be omitted (as in your example). The last one is a ViewBuilder
and has no default value, so needs to be provided.
When you look at a definition of a ViewBuilder
it takes between 0 and 10 Views:
static func buildBlock() -> EmptyView
static func buildBlock<Content>(Content) -> Content
static func buildBlock<C0, C1>(C0, C1) -> TupleView<(C0, C1)>
static func buildBlock<C0, C1, C2>(C0, C1, C2) -> TupleView<(C0, C1, C2)>
/....
static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9>(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9) -> TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>
Long story short, what you see between { .. } is a ViewBuilder
, which is a closure, that is part of the initializer.
The kind of VStack
syntax is possible thanks to a new feature in Swift 5.1 called Function Builder.
Without function builders, the code would look like that:
var body: some View {
var builder = VStackBuilder()
builder.add(Image(uiImage: image))
builder.add(Text(title))
builder.add(Text(subtitle))
return builder.build()
}