@ViewBuilder — یک حاشیهنویسی result builder در SwiftUI است که برای ساخت اعلامی سلسلهمراتب View طراحی شده است. بر اساس Apple Developer Documentation, 2024، @ViewBuilder یک بلوک کد با عبارات متعدد و منطق شرطی را به یک نوع View واحد تبدیل میکند که برای کامپایلر Swift قابل فهم است. بدون این حاشیهنویسی، استفاده از نحو اعلامی آشنا SwiftUI با if/else و چندین عنصر در بدنه body غیرممکن بود.
نکات اصلی
@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 خود ایجاد، بهروزرسانی و حذف عناصر را بر اساس تغییرات حالت مدیریت میکند.
Result builder — مکانیزم Swift است که دنبالهای از عبارات را از طریق متدهای استاتیک buildBlock، buildOptional، buildEither و دیگران به یک مقدار ترکیبی تبدیل میکند. وقتی کامپایلر حاشیهنویسی @ViewBuilder را میبیند، به طور خودکار این متدها را در فرآیند کامپایل بر بلوک کد اعمال میکند.
@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 فراهم میکند.
در SwiftUI، ویژگی body به طور ضمنی با @ViewBuilder حاشیهنویسی شده است — شما این حاشیهنویسی را در کد نمیبینید، اما کامپایلر آن را به طور خودکار اعمال میکند. با این حال، برای ویژگیهای سفارشی که چندین View برمیگردانند یا پارامترهای بستهکننده، حاشیهنویسی باید به صراحت مشخص شود.
محدودیت ۱ — ۱۰ عنصر در یک بلوک. این معروفترین محدودیت @ViewBuilder است. اگر نیاز به نمایش بیش از ۱۰ عنصر در یک سطح دارید، کامپایلر خطا میدهد. میتوانید با Group، ForEach، List یا تقسیم به زیرکامپوننتها این محدودیت را دور بزنید. Group تودرتویی بصری اضافه نمیکند، اما هر Group یک عنصر محسوب میشود.
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.
الگوی ۱: نمایش شرطی با if/else. رایجترین سناریوی استفاده از @ViewBuilder. امکان نمایش Viewهای مختلف بسته به وضعیت بدون استفاده از عملگرهای سهتایی یا روشهای کارخانهای.
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 است.
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 حاشیهنویسی شده و some View برمیگرداند. چنین توابعی امکان کپسولهسازی منطق نمایش پیچیده و استفاده مجدد از آن در بخشهای مختلف برنامه را فراهم میکنند.
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 — یک حاشیهنویسی result builder است که بلوک کد با عبارات متعدد و شرایط را به یک نوع View واحد تبدیل میکند. این امکان استفاده از نحو آشنا Swift (if/else، switch، عبارات اختیاری) را در داخل UI اعلامی SwiftUI فراهم میکند.
محدودیت به پیادهسازی buildBlock مربوط است — برای هر تعداد آرگومان از ۱ تا ۱۰ یک overload مجزا از متد وجود دارد. Swift از variadic generics پشتیبانی نمیکند، بنابراین تعداد overloadها ثابت است. برای دور زدن این محدودیت از Group، ForEach یا زیرکامپوننتها استفاده کنید.
خیر، پروتکل View به طور ضمنی @ViewBuilder را به ویژگی body اعمال میکند. با این حال، برای ویژگیهای سفارشی، متدها و پارامترهای بستهکنندهای که چندین View برمیگردانند، حاشیهنویسی باید به صراحت مشخص شود. بدون آن، کامپایلر نمیتواند عبارات متعدد را پردازش کند.
برای عبارات اختیاری از متد buildIf استفاده میشود که یک View اختیاری دریافت میکند و در صورت وجود مقدار، آن را برمیگرداند. اگر مقدار nil باشد — buildIf nil برمیگرداند و عنصر نمایش داده نمیشود. این امکان استفاده از if let را در بدنه body فراهم میکند.
بله، از Swift 5.9 @ViewBuilder از switch از طریق متد buildExpression پشتیبانی میکند. کامپایلر هر شاخه case را به فراخوانی buildEither مربوطه تبدیل میکند. پشتیبانی از switch کد را در مقایسه با ساختارهای تو در توی if/else خواناتر میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید