@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 که چندین View را بدون کانتینرهای اضافی در یک ترکیب جمع می‌کند
  • buildBlock — دنباله‌ای از عبارات را در TupleView تا ۱۰ عنصر می‌پیچد
  • buildEither — برای شاخه‌های if/else و switch ConditionalContent ایجاد می‌کند
  • محدودیت — تا ۱۰ عنصر در یک بلوک بدون Group یا ForEach
  • کاربرد ضمنی — body قبلاً در @ViewBuilder پیچیده شده است، توابع سفارشی نیاز به حاشیه‌نویسی صریح دارند

@ViewBuilder در SwiftUI چیست؟

@ViewBuilder — یک حاشیه‌نویسی است که الگوی result builder (SE-0289) را پیاده‌سازی می‌کند و به SwiftUI اجازه می‌دهد چندین View را با استفاده از نحو اعلامی در یک ترکیب جمع کند. این حاشیه‌نویسی به طور خودکار عبارات متعدد، ساختارهای شرطی و مقادیر اختیاری را در انواع مربوطه می‌پیچد: TupleView، ConditionalContent، OptionalContent.

پیش از ظهور result builder، توسعه‌دهندگان مجبور بودند عناصر را به صورت دستی در VStack یا HStack بپیچند و برای منطق شرطی از عملگرهای سه‌تایی یا روش‌های کارخانه‌ای استفاده کنند. @ViewBuilder نحو SwiftUI را مختصر و خوانا کرد و امکان نوشتن کدی را فراهم کرد که مانند Swift معمولی با if/else و حلقه‌ها به نظر می‌رسد.

بر اساس Swift Evolution SE-0289، result builders یک مکانیزم عمومی هستند که به SwiftUI محدود نمی‌شوند. @ViewBuilder یکی از پیاده‌سازی‌های این مکانیزم است، در کنار @StringBuilder برای ساخت رشته‌ها و پیاده‌سازی‌های کتابخانه‌ای برای DSLهای دیگر. در SwiftUI از @ViewBuilder نه تنها برای body، بلکه برای پارامترهای بسته‌کننده کانتینرها نیز استفاده می‌شود (VStack، HStack، ZStack، List).

تفاوت با رویکرد امری

در UIKit امری، شما به صورت امری UIView ایجاد می‌کنید، ویژگی‌های آن را پیکربندی می‌کنید و از طریق addSubview به سلسله‌مراتب اضافه می‌کنید. در SwiftUI با @ViewBuilder، شما به صورت اعلامی توصیف می‌کنید که کدام Viewها باید نمایش داده شوند و 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 از ۱ تا ۱۰ عبارت دریافت می‌کند و TupleView برمی‌گرداند. هر تعداد عبارت overload مخصوص buildBlock خود را دارد: از buildBlock<C0> تا buildBlock<C0, C1, ..., C9>. به همین دلیل تعداد عناصر در یک بلوک @ViewBuilder به ۱۰ محدود شده است.

buildEither (first/second) ساختارهای if/else را پردازش می‌کند. هر شاخه به متد مربوطه ارسال می‌شود و نتیجه در ConditionalContent پیچیده می‌شود — نوعی که انواع خاص شاخه‌ها را پنهان می‌کند و یک رابط یکپارچه برای SwiftUI فراهم می‌کند.

کار ضمنی @ViewBuilder

در SwiftUI، ویژگی body به طور ضمنی با @ViewBuilder حاشیه‌نویسی شده است — شما این حاشیه‌نویسی را در کد نمی‌بینید، اما کامپایلر آن را به طور خودکار اعمال می‌کند. با این حال، برای ویژگی‌های سفارشی که چندین View برمی‌گردانند یا پارامترهای بسته‌کننده، حاشیه‌نویسی باید به صراحت مشخص شود.

محدودیت‌های @ViewBuilder و نحوه دور زدن آنها

محدودیت ۱ — ۱۰ عنصر در یک بلوک. این معروف‌ترین محدودیت @ViewBuilder است. اگر نیاز به نمایش بیش از ۱۰ عنصر در یک سطح دارید، کامپایلر خطا می‌دهد. می‌توانید با 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")
        }
    }
}

محدودیت ۲ — عدم پشتیبانی از برخی ساختارها. @ViewBuilder از do/catch، guard، for-in (بدون ForEach) و سایر ساختارهای کنترلی پشتیبانی نمی‌کند. برای حلقه‌ها از ForEach با داده‌های قابل شناسایی استفاده کنید. برای مدیریت خطا از Viewهای جداگانه‌ای استفاده کنید که Result یا مقادیر اختیاری دریافت می‌کنند.

محدودیت ۳ — دشواری اشکال‌زدایی. در صورت خطا در @ViewBuilder، کامپایلر پیام‌های طولانی و مبهمی تولید می‌کند که یافتن علت اصلی در آنها دشوار است. مشکلات رایج: عدم تطابق نوع در شاخه‌های if/else، تجاوز از محدودیت ۱۰ عنصر یا عدم وجود overload مورد نیاز buildBlock.

الگوهای استفاده از @ViewBuilder

الگوی ۱: نمایش شرطی با if/else. رایج‌ترین سناریوی استفاده از @ViewBuilder. امکان نمایش Viewهای مختلف بسته به وضعیت بدون استفاده از عملگرهای سه‌تایی یا روش‌های کارخانه‌ای.

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

الگوی ۲: @ViewBuilder در پارامترهای توابع و مقداردهنده‌ها. برای ایجاد کانتینرهای قابل استفاده مجدد که Viewهای فرزند را از طریق بسته دریافت می‌کنند. این الگوی استاندارد برای کتابخانه‌ها و کامپوننت‌های 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)
    }
}

الگوی ۳: ترکیب با 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. فقط نوع opaque امکان پنهان کردن پیاده‌سازی خاص و حفظ انعطاف‌پذیری ترکیب را فراهم می‌کند.

عملکرد: توابع سفارشی @ViewBuilder در مقایسه با کد مستقیم در body سربار اضافی ندارند. کامپایلر فراخوانی‌ها را inline می‌کند و کد حاصل را بهینه می‌سازد. تقسیم body به توابع @ViewBuilder خوانایی را بدون کاهش عملکرد بهبود می‌بخشد.

سوالات متداول

@ViewBuilder در SwiftUI چیست؟

@ViewBuilder — یک حاشیه‌نویسی result builder است که بلوک کد با عبارات متعدد و شرایط را به یک نوع View واحد تبدیل می‌کند. این امکان استفاده از نحو آشنا Swift (if/else، switch، عبارات اختیاری) را در داخل UI اعلامی SwiftUI فراهم می‌کند.

چرا نمی‌توان بیش از ۱۰ عنصر در @ViewBuilder قرار داد؟

محدودیت به پیاده‌سازی buildBlock مربوط است — برای هر تعداد آرگومان از ۱ تا ۱۰ یک overload مجزا از متد وجود دارد. Swift از variadic generics پشتیبانی نمی‌کند، بنابراین تعداد overloadها ثابت است. برای دور زدن این محدودیت از Group، ForEach یا زیرکامپوننت‌ها استفاده کنید.

آیا باید @ViewBuilder را قبل از body به صراحت مشخص کرد؟

خیر، پروتکل View به طور ضمنی @ViewBuilder را به ویژگی body اعمال می‌کند. با این حال، برای ویژگی‌های سفارشی، متدها و پارامترهای بسته‌کننده‌ای که چندین View برمی‌گردانند، حاشیه‌نویسی باید به صراحت مشخص شود. بدون آن، کامپایلر نمی‌تواند عبارات متعدد را پردازش کند.

@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 می‌پیچد (تا ۱۰ عنصر)
  • buildEither برای شاخه‌های if/else و switch ConditionalContent ایجاد می‌کند
  • buildIf عبارات اختیاری و if بدون else را پردازش می‌کند
  • Group و ForEach به دور زدن محدودیت ۱۰ عنصر در یک بلوک کمک می‌کنند
  • توابع سفارشی @ViewBuilder استفاده مجدد را بدون کاهش عملکرد بهبود می‌بخشند
  • @ViewBuilder به طور ضمنی برای body اعمال می‌شود، اما برای پارامترها نیاز به حاشیه‌نویسی صریح دارد

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید