@ViewBuilder: nó là gì, result builder cho View trong SwiftUI

Tác giả: IT Sectr Đã đăng: 2026-06-24 Thời gian đọc: 7 phút

@ViewBuilder là một chú thích result builder trong SwiftUI được thiết kế để xây dựng hệ phân cấp View theo cách khai báo. Theo Apple Developer Documentation, 2024, @ViewBuilder biến đổi một khối mã với nhiều biểu thức và logic điều kiện thành một kiểu View duy nhất mà trình biên dịch Swift có thể hiểu được. Nếu không có chú thích này, sẽ không thể sử dụng cú pháp SwiftUI khai báo quen thuộc với if/else và nhiều phần tử trong body.

Những điểm chính

  • @ViewBuilder — result builder kết hợp nhiều View thành một tổ hợp mà không cần thêm container
  • buildBlock — bọc một chuỗi các biểu thức vào TupleView với tối đa 10 phần tử
  • buildEither — tạo ConditionalContent cho các nhánh if/else và switch
  • Giới hạn — tối đa 10 phần tử trong một khối mà không có Group hoặc ForEach
  • Áp dụng ngầm định — body đã được bọc trong @ViewBuilder, các hàm tùy chỉnh yêu cầu chú thích rõ ràng

@ViewBuilder trong SwiftUI là gì?

@ViewBuilder là một chú thích triển khai mẫu result builder (SE-0289), cho phép SwiftUI kết hợp nhiều View thành một tổ hợp duy nhất bằng cú pháp khai báo. Nó tự động bọc nhiều biểu thức, cấu trúc điều kiện và giá trị tùy chọn vào các kiểu tương ứng của chúng: TupleView, ConditionalContent, OptionalContent.

Trước khi result builder ra đời, các nhà phát triển phải tự tay bọc các phần tử trong VStack hoặc HStack, và sử dụng toán tử ba ngôi hoặc phương thức factory cho logic điều kiện. @ViewBuilder đã làm cho cú pháp SwiftUI trở nên ngắn gọn và dễ đọc, cho phép viết mã trông giống như Swift thông thường với if/else và vòng lặp.

Theo Swift Evolution SE-0289, result builders là một cơ chế chung không gắn liền với SwiftUI. @ViewBuilder là một triển khai của cơ chế này, cùng với @StringBuilder để xây dựng chuỗi và các triển khai thư viện cho các DSL khác. Trong SwiftUI, @ViewBuilder không chỉ được sử dụng cho body mà còn cho các tham số closure của container (VStack, HStack, ZStack, List).

Sự khác biệt so với cách tiếp cận mệnh lệnh

Trong UIKit mệnh lệnh, bạn tạo rõ ràng một UIView, cấu hình các thuộc tính của nó và thêm nó vào hệ phân cấp qua addSubview. Trong SwiftUI với @ViewBuilder, bạn mô tả một cách khai báo những View nào sẽ được hiển thị, và SwiftUI quản lý việc tạo, cập nhật và xóa các phần tử dựa trên các thay đổi trạng thái.

@ViewBuilder hoạt động như thế nào: result builder

Result builder là một cơ chế Swift biến đổi một chuỗi các biểu thức thành một giá trị tổng hợp duy nhất thông qua các phương thức tĩnh buildBlock, buildOptional, buildEither và các phương thức khác. Khi trình biên dịch nhìn thấy chú thích @ViewBuilder, nó tự động áp dụng các phương thức này vào khối mã trong quá trình biên dịch.

swift
@resultBuilder
struct ViewBuilder {
    static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1) -> TupleView<(C0, C1)>
    static func buildIf<C>(_ c: C?) -> C?
    static func buildEither<T, F>(first: T) -> ConditionalContent<T, F>
    static func buildEither<T, F>(second: F) -> ConditionalContent<T, F>
}

buildBlock chấp nhận từ 1 đến 10 biểu thức và trả về một TupleView. Mỗi số lượng (số lượng biểu thức) có overload buildBlock riêng: từ buildBlock đến buildBlock. Đây là lý do tại sao số lượng phần tử trong một khối @ViewBuilder bị giới hạn ở 10.

buildEither (first/second) xử lý các cấu trúc if/else. Mỗi nhánh được truyền vào phương thức tương ứng, và kết quả được bọc trong ConditionalContent — một kiểu ẩn các kiểu nhánh cụ thể và cung cấp một giao diện thống nhất cho SwiftUI.

Hành vi ngầm định của @ViewBuilder

Trong SwiftUI, thuộc tính body đã được chú thích ngầm định với @ViewBuilder — bạn không thấy chú thích này trong mã, nhưng trình biên dịch tự động áp dụng nó. Tuy nhiên, đối với các thuộc tính tùy chỉnh trả về nhiều View, hoặc cho các tham số closure, chú thích phải được chỉ định rõ ràng.

Giới hạn của @ViewBuilder và cách khắc phục

Giới hạn 1 — 10 phần tử trong một khối. Đây là giới hạn nổi tiếng nhất của @ViewBuilder. Nếu bạn cần hiển thị hơn 10 phần tử ở cùng một cấp, trình biên dịch sẽ báo lỗi. Các giải pháp bao gồm Group, ForEach, List hoặc chia thành các thành phần phụ. Group không thêm sự lồng nhau trực quan, nhưng mỗi Group được tính là một phần tử.

swift
struct ManyElementsView: View {
    var body: some View {
        Group {
            Text("1"); Text("2"); Text("3")
            Text("4"); Text("5"); Text("6")
            Text("7"); Text("8"); Text("9")
        }
        Group {
            Text("10"); Text("11"); Text("12")
        }
    }
}

Giới hạn 2 — thiếu hỗ trợ cho một số cấu trúc. @ViewBuilder không hỗ trợ do/catch, guard, for-in (không có ForEach) và các cấu trúc điều khiển luồng khác. Đối với vòng lặp, hãy sử dụng ForEach với dữ liệu có thể xác định. Để xử lý lỗi, hãy sử dụng các View riêng biệt chấp nhận Result hoặc giá trị tùy chọn.

Giới hạn 3 — độ phức tạp khi gỡ lỗi. Khi xảy ra lỗi trong @ViewBuilder, trình biên dịch tạo ra các thông báo dài dòng khó tìm ra nguyên nhân gốc. Các vấn đề điển hình: không khớp kiểu trong nhánh if/else, vượt quá giới hạn 10 phần tử hoặc thiếu các overload buildBlock cần thiết.

Các mẫu sử dụng @ViewBuilder

Mẫu 1: hiển thị có điều kiện qua if/else. Trường hợp sử dụng phổ biến nhất của @ViewBuilder. Cho phép hiển thị các View khác nhau dựa trên trạng thái mà không cần sử dụng toán tử ba ngôi hoặc phương thức factory.

swift
struct StatusView: View {
    var status: LoadStatus

    @ViewBuilder
    var body: some View {
        switch status {
        case .loading:
            ProgressView("Loading...")
        case .loaded(let data):
            DataView(data: data)
        case .error(let message):
            ErrorView(message: message)
        }
    }
}

Mẫu 2: @ViewBuilder trong tham số hàm và bộ khởi tạo. Được sử dụng để tạo các container tái sử dụng chấp nhận các View con thông qua closure. Đây là mẫu tiêu chuẩn cho các thư viện và thành phần UI.

swift
struct SectionCard<Content: View>: View {
    let title: String
    @ViewBuilder let content: Content

    var body: some View {
        VStack(alignment: .leading) {
            Text(title).font(.headline)
            content
        }
        .padding()
        .background(Color.gray.opacity(0.1))
        .cornerRadius(12)
    }
}

Mẫu 3: kết hợp với ForEach. @ViewBuilder hoạt động chính xác với ForEach, cho phép tạo động các phần tử từ một mảng dữ liệu. Mỗi phần tử của ForEach được tính là một biểu thức trong ngữ cảnh của @ViewBuilder.

Tạo ViewBuilder tùy chỉnh cho các thành phần tái sử dụng

ViewBuilder tùy chỉnh là một hàm hoặc thuộc tính do người dùng định nghĩa được chú thích với @ViewBuilder và trả về some View. Các hàm như vậy cho phép đóng gói logic hiển thị phức tạp và tái sử dụng nó trong các phần khác nhau của ứng dụng.

swift
struct FormRow<Content: View>: View {
    let label: String
    @ViewBuilder let content: Content

    var body: some View {
        HStack {
            Text(label)
                .frame(width: 120, alignment: .trailing)
            content
        }
    }
}

// Sử dụng:
FormRow(label: "Name") {
    TextField("Enter name", text: $name)
}

FormRow(label: "Gender") {
    Picker("Select", selection: $gender) {
        Text("Nam").tag(Gender.male)
        Text("Nữ").tag(Gender.female)
    }
}

Quy tắc quan trọng: một hàm tùy chỉnh với @ViewBuilder phải trả về some View, không phải một kiểu cụ thể hay giao thức View. Chỉ kiểu không minh bạch mới cho phép ẩn triển khai cụ thể trong khi vẫn giữ được tính linh hoạt của việc kết hợp.

Hiệu suất: các hàm @ViewBuilder tùy chỉnh không thêm chi phí so với mã trực tiếp trong body. Trình biên dịch inline các lời gọi và tối ưu hóa mã kết quả. Chia body thành các hàm @ViewBuilder cải thiện khả năng đọc mà không làm giảm hiệu suất.

Câu hỏi thường gặp

@ViewBuilder trong SwiftUI là gì?

@ViewBuilder là một chú thích result builder biến đổi một khối mã với nhiều biểu thức và điều kiện thành một kiểu View duy nhất. Nó cho phép sử dụng cú pháp Swift quen thuộc (if/else, switch, biểu thức tùy chọn) bên trong UI khai báo của SwiftUI.

Tại sao không thể đặt hơn 10 phần tử trong @ViewBuilder?

Giới hạn bắt nguồn từ triển khai của buildBlock — có một overload riêng của phương thức cho mỗi số lượng từ 1 đến 10. Swift không hỗ trợ generics variadic, vì vậy số lượng overload là cố định. Để khắc phục, hãy sử dụng Group, ForEach hoặc các thành phần phụ.

Tôi có cần chỉ định rõ ràng @ViewBuilder trước body không?

Không, giao thức View áp dụng ngầm định @ViewBuilder cho thuộc tính body. Tuy nhiên, đối với các thuộc tính tùy chỉnh, phương thức và tham số closure trả về nhiều View, chú thích phải được chỉ định rõ ràng. Nếu không có nó, trình biên dịch sẽ không thể xử lý nhiều biểu thức.

@ViewBuilder xử lý các biểu thức tùy chọn như thế nào?

Đối với biểu thức tùy chọn, phương thức buildIf được sử dụng, phương thức này chấp nhận một View tùy chọn và trả về nó nếu có giá trị. Nếu giá trị là nil, buildIf trả về nil và phần tử không được hiển thị. Điều này cho phép sử dụng if let bên trong body.

Có thể sử dụng @ViewBuilder với switch không?

Có, kể từ Swift 5.9 @ViewBuilder hỗ trợ switch thông qua phương thức buildExpression. Trình biên dịch biến đổi mỗi nhánh case thành lời gọi buildEither tương ứng. Hỗ trợ switch làm cho mã dễ đọc hơn so với các cấu trúc if/else lồng nhau.

Tổng kết

  • @ViewBuilder — result builder để xây dựng hệ phân cấp View theo cách khai báo trong SwiftUI
  • buildBlock bọc một chuỗi biểu thức vào TupleView (tối đa 10 phần tử)
  • buildEither tạo ConditionalContent cho các nhánh if/else và switch
  • buildIf xử lý các biểu thức tùy chọn và if không có else
  • Group và ForEach giúp khắc phục giới hạn 10 phần tử mỗi khối
  • Các hàm @ViewBuilder tùy chỉnh cải thiện khả năng tái sử dụng mà không mất hiệu suất
  • @ViewBuilder được áp dụng ngầm định cho body, nhưng yêu cầu chú thích rõ ràng cho tham số

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm