First, when you see it used in code, or when you want to use it. We can most likely infer that the basic SwiftUI elements and components are no longer able to meet the complex requirements or customization requirements of our project, so we need to DIY a more scalable View.

So today we’re going to get a good stroke of this at sign ViewBuilder

import SwiftUI

struct ContentView: View {
    var body: some View {
        HStack{
            Text("hello")
            Text("world")
        }
    }
}
Copy the code

Very simple Hello World SwiftUI code right? First of all, in the SwiftUI world, everything is a View, but have you ever taken a picture, why we put two Text views in the HStack View, they can be aligned horizontally, why HStack can accept two views? Let’s click on HStack’s Definition to see:

@inlinable public init(
    alignment: VerticalAlignment = .center,
    spacing: CGFloat? = nil,
    @ViewBuilder content: () -> Content
)
Copy the code

As you can see from the source, init has a closure content that is embellished by @ViewBuilder, which means that the expressions inside this closure need to be handled by @ViewBuilder. The static buildBlock method in the @ViewBuilder structure will be used to build the @viewBuilder closure. The static buildBlock method takes two views as arguments.

@available(iOS 13.0, OSX 13.0, tvOS 13.0, watchOS 6.0, *) Extension ViewBuilder {public static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1) -> TupleView<(C0, C1)> where C0 : View, C1 : View }Copy the code

From the ViewBuilder source, we can see that it takes two views as input arguments and returns a TupleView that combines the two views.

If you read down the source code, there are other declarations:

@available(iOS 13.0, macOS 13.0, tvOS 13.0, watchOS 6.0, *) Extension ViewBuilder {public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9) -> TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)> where C0 : View, C1 : View, C2 : View, C3 : View, C4 : View, C5 : View, C6 : View, C7 : View, C8 : View, C9 : View }Copy the code

This tells us that with the current SwiftUI version, @ViewBuilder can only accept ten views inside a closure.

What is a TupleView?

A TupleView is a view created from a quick tuple of view values. There is no logic inside the TupleView. It only preserves views. The TupleView is completely transparent and behaves like its parent view. This means that when you put it in the HStack, the TupleView will place the view from the tuple horizontally.

OK, now that we know what @ViewBuilder does, let’s think about how to use it.

Suppose we want to make notification function for our app, first of all, we think that the Notification should be componentized, but the content of our Notification should be diversified and customized. At this point, we can use @ViewBuilder

import SwiftUI

struct NotificationView<Content: View>: View {
    let content: Content

    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }

    var body: some View {
        content
            .padding()
            .background(Color(.tertiarySystemBackground))
            .cornerRadius(16)
            .transition(.move(edge: .top))
            .animation(.spring())
    }
}
Copy the code

We’ve got a NotificationView right here. In this View, we’ve drawn the basic style of the Notification, but the content type is a closure decorated with @ViewBuilder. This sets the stage for customizing our push content later.

When we want to use the NotificationView, we can do this:

import SwiftUI struct ContentView: View { @State private var notificationShown = false var body: some View { VStack { if self.notificationShown { NotificationView { Text("notification") } } Spacer() Button("toggle") {  self.notificationShown.toggle() } Spacer() } } }Copy the code