@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، واستخدام العوامل الثلاثية أو طرق المصنع للمنطق الشرطي. جعل @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، تصف بشكل تصريحي أي 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. كل عدد (عدد التعبيرات) له تحميل زائد خاص به لـ buildBlock: من buildBlock إلى buildBlock. لهذا السبب فإن عدد العناصر في كتلة @ViewBuilder واحدة محدود بـ 10.

buildEither (first/second) يعالج إنشاءات if/else. يتم تمرير كل فرع إلى الطريقة المقابلة، ويتم لف النتيجة في ConditionalContent — وهو نوع يخفي الأنواع المحددة للفروع ويوفر واجهة موحدة لـ SwiftUI.

السلوك الضمني لـ @ViewBuilder

في SwiftUI، الخاصية body مُعلَّقة ضمنيًا بـ @ViewBuilder — لا ترى هذا التعليق في الكود، لكن المترجم يطبقه تلقائيًا. ومع ذلك، للخصائص المخصصة التي تعيد عدة Views، أو لمعاملات الإغلاق، يجب تحديد التعليق بشكل صريح.

قيود @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 عناصر أو عدم وجود التحميلات الزائدة المطلوبة لـ buildBlock.

أنماط استخدام @ViewBuilder

النمط 1: العرض الشرطي عبر if/else. حالة الاستخدام الأكثر شيوعًا لـ @ViewBuilder. يسمح بعرض Views مختلفة حسب الحالة دون استخدام العوامل الثلاثية أو طرق المصنع.

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 فرعية عبر إغلاق. هذا هو النمط القياسي للمكتبات ومكونات واجهة المستخدم.

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. يقوم المترجم بدمج الاستدعاءات وتحسين الكود الناتج. تقسيم body إلى دوال @ViewBuilder يحسن قابلية القراءة دون التضحية بالأداء.

الأسئلة الشائعة

ما هو @ViewBuilder في SwiftUI؟

@ViewBuilder هو تعليق result builder يحول كتلة من الكود بتعبيرات متعددة وشروط إلى نوع View واحد. يسمح باستخدام صيغة Swift المألوفة (if/else, switch, التعبيرات الاختيارية) داخل واجهة SwiftUI التصريحية.

لماذا لا يمكن وضع أكثر من 10 عناصر في @ViewBuilder؟

القيود ناتج عن تنفيذ buildBlock — يوجد تحميل زائد منفصل للطريقة لكل عدد من 1 إلى 10. لا تدعم Swift الأنواع العامة المتغيرة العدد، لذلك فإن عدد التحميلات الزائدة ثابت. لتجاوز ذلك، استخدم Group أو ForEach أو مكونات فرعية.

هل أحتاج إلى تحديد @ViewBuilder صراحةً قبل body؟

لا، بروتوكول View يطبق ضمنيًا @ViewBuilder على الخاصية body. ومع ذلك، للخصائص المخصصة والطرق ومعاملات الإغلاق التي تعيد عدة Views، يجب تحديد التعليق صراحة. بدونها، لن يتمكن المترجم من معالجة التعبيرات المتعددة.

كيف يتعامل @ViewBuilder مع التعبيرات الاختيارية؟

للتعبيرات الاختيارية، تُستخدم طريقة buildIf، التي تقبل View اختياريًا وتعيده إذا كانت هناك قيمة. إذا كانت القيمة nil، تعيد buildIf nil ولا يتم عرض العنصر. يتيح ذلك استخدام if let داخل body.

هل يمكن استخدام @ViewBuilder مع switch؟

نعم، منذ Swift 5.9 يدعم @ViewBuilder switch من خلال طريقة buildExpression. يحول المترجم كل فرع case إلى استدعاء buildElse المقابل. دعم 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. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا