@ViewBuilder: คืออะไร, result builder สำหรับ View ใน SwiftUI

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-06-24 เวลาอ่าน: 7 นาที

@ViewBuilder คือคำอธิบายประกอบ result builder ใน SwiftUI ที่ออกแบบมาสำหรับการสร้างลำดับชั้น View แบบประกาศ ตาม Apple Developer Documentation, 2024 @ViewBuilder จะแปลงบล็อกโค้ดที่มีนิพจน์หลายรายการและตรรกะแบบมีเงื่อนไขให้เป็นชนิด View เดียวที่คอมไพเลอร์ Swift สามารถเข้าใจได้ หากไม่มีคำอธิบายประกอบนี้ ก็จะเป็นไปไม่ได้ที่จะใช้ไวยากรณ์ประกาศของ SwiftUI ที่คุ้นเคยกับ if/else และองค์ประกอบหลายรายการใน body

ประเด็นสำคัญ

  • @ViewBuilder — result builder ที่รวมหลาย Views เป็นองค์ประกอบเดียวโดยไม่ต้องมีคอนเทนเนอร์เพิ่มเติม
  • buildBlock — ห่อลำดับของนิพจน์เป็น TupleView สูงสุด 10 องค์ประกอบ
  • buildEither — สร้าง ConditionalContent สำหรับสาขา if/else และ switch
  • ข้อจำกัด — สูงสุด 10 องค์ประกอบในบล็อกเดียวโดยไม่มี Group หรือ ForEach
  • การใช้งานโดยนัย — body ถูกห่อใน @ViewBuilder อยู่แล้ว ฟังก์ชันที่กำหนดเองต้องการคำอธิบายประกอบที่ชัดเจน

@ViewBuilder ใน SwiftUI คืออะไร?

@ViewBuilder คือคำอธิบายประกอบที่ใช้รูปแบบ result builder (SE-0289) ซึ่งช่วยให้ SwiftUI รวมหลาย Views เป็นองค์ประกอบเดียวโดยใช้ไวยากรณ์แบบประกาศ มันจะห่อนิพจน์หลายรายการ โครงสร้างแบบมีเงื่อนไข และค่าเลือกได้เป็นชนิดที่สอดคล้องกันโดยอัตโนมัติ: TupleView, ConditionalContent, OptionalContent

ก่อนที่ result builders จะมีขึ้น นักพัฒนาต้องห่อองค์ประกอบด้วยตนเองใน VStack หรือ HStack และใช้ตัวดำเนินการ ternary หรือเมธอด factory สำหรับตรรกะแบบมีเงื่อนไข @ViewBuilder ทำให้ไวยากรณ์ของ SwiftUI กระชับและอ่านง่าย ช่วยให้เขียนโค้ดที่ดูเหมือน Swift ปกติกับ if/else และลูป

ตาม Swift Evolution SE-0289 result builders เป็นกลไกทั่วไปที่ไม่ผูกติดกับ SwiftUI @ViewBuilder เป็นหนึ่งในการใช้งานของกลไกนี้ พร้อมกับ @StringBuilder สำหรับการสร้างสตริงและการใช้งานไลบรารีสำหรับ DSL อื่นๆ ใน SwiftUI @ViewBuilder ไม่ได้ใช้เพียงสำหรับ body เท่านั้น แต่ยังใช้สำหรับพารามิเตอร์ closure ของคอนเทนเนอร์ (VStack, HStack, ZStack, List) ด้วย

ความแตกต่างจากแนวทางเชิงคำสั่ง

ใน UIKit เชิงคำสั่ง คุณสร้าง UIView อย่างชัดเจน กำหนดค่าคุณสมบัติของมัน และเพิ่มลงในลำดับชั้นผ่าน addSubview ใน SwiftUI กับ @ViewBuilder คุณอธิบายอย่างประกาศว่าควรแสดง Views ใดบ้าง และ SwiftUI จะจัดการการสร้าง การอัปเดต และการลบองค์ประกอบตามการเปลี่ยนแปลงสถานะ

@ViewBuilder ทำงานอย่างไร: result builder

Result builder คือกลไกของ Swift ที่แปลงลำดับของนิพจน์เป็นค่าเชิงประกอบเดียวผ่านเมธอดสแตติก buildBlock, buildOptional, buildEither และอื่นๆ เมื่อคอมไพเลอร์เห็นคำอธิบายประกอบ @ViewBuilder มันจะใช้เมธอดเหล่านี้กับบล็อกโค้ดโดยอัตโนมัติระหว่างการคอมไพล์

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 รับนิพจน์ 1 ถึง 10 รายการและส่งคืน TupleView แต่ละอาริตี้ (จำนวนนิพจน์) มี overload buildBlock ของตัวเอง: จาก buildBlock ถึง buildBlock นี่คือเหตุผลที่จำนวนองค์ประกอบในบล็อก @ViewBuilder เดียวถูกจำกัดไว้ที่ 10

buildEither (first/second) จัดการกับโครงสร้าง if/else แต่ละสาขาจะถูกส่งไปยังเมธอดที่เกี่ยวข้อง และผลลัพธ์จะถูกห่อใน ConditionalContent — ชนิดที่ซ่อนชนิดสาขาเฉพาะและให้อินเทอร์เฟซแบบรวมสำหรับ SwiftUI

พฤติกรรมโดยนัยของ @ViewBuilder

ใน SwiftUI คุณสมบัติ body ถูกอธิบายประกอบด้วย @ViewBuilder โดยนัยอยู่แล้ว — คุณไม่เห็นคำอธิบายประกอบนี้ในโค้ด แต่คอมไพเลอร์จะใช้มันโดยอัตโนมัติ อย่างไรก็ตาม สำหรับคุณสมบัติที่กำหนดเองที่ส่งคืนหลาย Views หรือสำหรับพารามิเตอร์ closure คำอธิบายประกอบต้องระบุอย่างชัดเจน

ข้อจำกัดของ @ViewBuilder และวิธีหลีกเลี่ยง

ข้อจำกัด 1 — 10 องค์ประกอบในบล็อก นี่คือข้อจำกัดที่รู้จักกันดีที่สุดของ @ViewBuilder หากคุณต้องการแสดงมากกว่า 10 องค์ประกอบในระดับเดียวกัน คอมไพเลอร์จะแสดงข้อผิดพลาด วิธีแก้ไขรวมถึง Group, ForEach, List หรือการแบ่งเป็นคอมโพเนนต์ย่อย Group ไม่เพิ่มการซ้อนด้วยภาพ แต่แต่ละ Group นับเป็นหนึ่งองค์ประกอบ

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")
        }
    }
}

ข้อจำกัด 2 — ขาดการสนับสนุนโครงสร้างบางอย่าง @ViewBuilder ไม่รองรับ do/catch, guard, for-in (ไม่มี ForEach) และโครงสร้างควบคุมการไหลอื่นๆ สำหรับลูป ให้ใช้ ForEach กับข้อมูลที่ระบุตัวตนได้ สำหรับการจัดการข้อผิดพลาด ให้ใช้ Views แยกต่างหากที่รับ Result หรือค่าเลือกได้

ข้อจำกัด 3 — ความซับซ้อนในการดีบัก เมื่อเกิดข้อผิดพลาดใน @ViewBuilder คอมไพเลอร์จะสร้างข้อความที่ยืดยาวซึ่งยากต่อการหาสาเหตุที่แท้จริง ปัญหาทั่วไป: ชนิดไม่ตรงกันในสาขา if/else, เกินขีดจำกัด 10 องค์ประกอบ หรือขาด overload buildBlock ที่จำเป็น

รูปแบบการใช้งาน @ViewBuilder

รูปแบบ 1: การแสดงผลแบบมีเงื่อนไขผ่าน if/else กรณีการใช้งานที่พบบ่อยที่สุดของ @ViewBuilder ช่วยให้แสดง Views ต่างกันตามสถานะโดยไม่ต้องใช้ตัวดำเนินการ ternary หรือเมธอด 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)
        }
    }
}

รูปแบบ 2: @ViewBuilder ในพารามิเตอร์ฟังก์ชันและตัวเริ่มต้น ใช้สำหรับสร้างคอนเทนเนอร์ที่ใช้ซ้ำได้ซึ่งรับ Views ลูกผ่าน closure นี่คือรูปแบบมาตรฐานสำหรับไลบรารีและคอมโพเนนต์ 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)
    }
}

รูปแบบ 3: การประกอบกับ ForEach @ViewBuilder ทำงานอย่างถูกต้องกับ ForEach ช่วยให้สร้างองค์ประกอบแบบไดนามิกจากอาร์เรย์ข้อมูล แต่ละองค์ประกอบของ ForEach นับเป็นหนึ่งนิพจน์ในบริบทของ @ViewBuilder

การสร้าง ViewBuilder แบบกำหนดเองสำหรับคอมโพเนนต์ที่ใช้ซ้ำได้

ViewBuilder แบบกำหนดเอง คือฟังก์ชันหรือคุณสมบัติที่ผู้ใช้กำหนดซึ่งอธิบายประกอบด้วย @ViewBuilder และส่งคืน some View ฟังก์ชันดังกล่าวช่วยให้ห่อหุ้มตรรกะการแสดงผลที่ซับซ้อนและนำกลับมาใช้ใหม่ในส่วนต่างๆ ของแอปพลิเคชัน

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
        }
    }
}

// การใช้งาน:
FormRow(label: "Name") {
    TextField("Enter name", text: $name)
}

FormRow(label: "Gender") {
    Picker("Select", selection: $gender) {
        Text("ชาย").tag(Gender.male)
        Text("หญิง").tag(Gender.female)
    }
}

กฎสำคัญ: ฟังก์ชันที่กำหนดเองกับ @ViewBuilder ต้องส่งคืน some View ไม่ใช่ชนิดที่เป็นรูปธรรมหรือโปรโตคอล View ชนิดทึบแสงเท่านั้นที่ช่วยให้ซ่อนการใช้งานที่เป็นรูปธรรมในขณะที่ยังคงความยืดหยุ่นในการประกอบ

ประสิทธิภาพ: ฟังก์ชัน @ViewBuilder แบบกำหนดเองไม่เพิ่มค่าใช้จ่ายเมื่อเทียบกับโค้ดโดยตรงใน body คอมไพเลอร์ทำ inline การเรียกและปรับโค้ดผลลัพธ์ให้เหมาะสม การแบ่ง body เป็นฟังก์ชัน @ViewBuilder ช่วยเพิ่มความสามารถในการอ่านโดยไม่เสียสละประสิทธิภาพ

คำถามที่พบบ่อย

@ViewBuilder ใน SwiftUI คืออะไร?

@ViewBuilder คือคำอธิบายประกอบ result builder ที่แปลงบล็อกโค้ดที่มีนิพจน์หลายรายการและเงื่อนไขเป็นชนิด View เดียว ช่วยให้ใช้ไวยากรณ์ Swift ที่คุ้นเคย (if/else, switch, นิพจน์เลือกได้) ภายใน UI แบบประกาศของ SwiftUI

ทำไมไม่สามารถใส่เกิน 10 องค์ประกอบใน @ViewBuilder?

ข้อจำกัดเกิดจากการใช้งาน buildBlock — มี overload แยกของเมธอดสำหรับแต่ละอาริตี้ตั้งแต่ 1 ถึง 10 Swift ไม่รองรับ generics แบบแปรผัน ดังนั้นจำนวน overload จึงคงที่ เพื่อหลีกเลี่ยง ให้ใช้ Group, ForEach หรือคอมโพเนนต์ย่อย

ฉันต้องระบุ @ViewBuilder อย่างชัดเจนก่อน body หรือไม่?

ไม่ โปรโตคอล View ใช้งานโดยนัย @ViewBuilder กับคุณสมบัติ body อย่างไรก็ตาม สำหรับคุณสมบัติที่กำหนดเอง เมธอด และพารามิเตอร์ closure ที่ส่งคืนหลาย Views คำอธิบายประกอบต้องระบุอย่างชัดเจน หากไม่มี คอมไพเลอร์จะไม่สามารถจัดการนิพจน์หลายรายการได้

@ViewBuilder จัดการนิพจน์เลือกได้อย่างไร?

สำหรับนิพจน์เลือกได้ จะใช้เมธอด buildIf ซึ่งรับ View แบบเลือกได้และส่งคืนหากมีค่า หากเป็น nil buildIf จะส่งคืน nil และองค์ประกอบจะไม่แสดง ซึ่งช่วยให้ใช้ if let ภายใน body

สามารถใช้ @ViewBuilder กับ switch ได้หรือไม่?

ได้ ตั้งแต่ Swift 5.9 @ViewBuilder รองรับ switch ผ่านเมธอด buildExpression คอมไพเลอร์จะแปลงแต่ละสาขา case เป็นการเรียก buildEither ที่สอดคล้องกัน การรองรับ switch ทำให้โค้ดอ่านง่ายขึ้นเมื่อเทียบกับโครงสร้าง if/else ที่ซ้อนกัน

สรุป

  • @ViewBuilder — result builder สำหรับการสร้างลำดับชั้น View แบบประกาศใน SwiftUI
  • buildBlock ห่อลำดับของนิพจน์เป็น TupleView (สูงสุด 10 องค์ประกอบ)
  • buildEither สร้าง ConditionalContent สำหรับสาขา if/else และ switch
  • buildIf จัดการนิพจน์เลือกได้และ if ที่ไม่มี else
  • Group และ ForEach ช่วยหลีกเลี่ยงข้อจำกัด 10 องค์ประกอบต่อบล็อก
  • ฟังก์ชัน @ViewBuilder แบบกำหนดเองช่วยเพิ่มความสามารถในการใช้ซ้ำโดยไม่สูญเสียประสิทธิภาพ
  • @ViewBuilder ถูกใช้งานโดยนัยกับ body แต่ต้องการคำอธิบายประกอบที่ชัดเจนสำหรับพารามิเตอร์

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม