Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to interpret VStack{ .. } from SwiftUI's official tutorial code? [duplicate]

Tags:

swift

swiftui

In the following code, I know

  1. body is a computed property which only have a get part.
  2. return is omitted for VStack

but what's { .. } after Vstack? is this Initialization or closures? and what's inside {}, nothing return i think.

struct ContentView: View {
    var body: some View {
        VStack {
            MapView()
                .frame(height: 300)

            CircleImage()
                .offset(y: -130)
                .padding(.bottom, -130)

            VStack(alignment: .leading) {
                Text("Turtle Rock")
                    .font(.title)
                HStack(alignment: .top) {
                    Text("Joshua Tree National Park")
                        .font(.subheadline)
                    Spacer()
                    Text("California")
                        .font(.subheadline)
                }
            }
            .padding()

            Spacer()
        }
    }
}

like image 731
stone Avatar asked Oct 28 '19 08:10

stone


People also ask

What is HStack and VStack?

HStack positions views in a horizontal line, VStack positions them in a vertical line, and ZStack overlays views on top of one another.

What is VStack in Swift?

VStack allows to arrange its child views in a vertical line, and ZStack allows to overlap its child views on top of each other. Stacks can further be customized with alignment and spacing in order to modify their appearance.

What is a VStack in Xcode?

A view that arranges its children in a vertical line.


3 Answers

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.

  1. MapView
  2. CircleImage
  3. another VStack
  4. 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.

like image 106
Sweeper Avatar answered Oct 24 '22 19:10

Sweeper


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.

like image 45
LuLuGaGa Avatar answered Oct 24 '22 19:10

LuLuGaGa


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()
}
like image 29
Eilon Avatar answered Oct 24 '22 19:10

Eilon