body: ما هو، الخاصية المحسوبة View في SwiftUI

المؤلف: IT Sectr نُشر: 2026-06-24 وقت القراءة: 7 دق

خاصية body هي العنصر المركزي لبروتوكول View في SwiftUI، وهي تحدد المحتوى الذي يظهر على الشاشة. وفقاً Apple Developer Documentation, 2024، body هو المتطلب الإلزامي الوحيد لبروتوكول View ويعيد نوعاً يطابق هذا البروتوكول نفسه. يستدعي SwiftUI الخاصية body عند كل تغيير في الحالة لبناء ومقارنة شجرة عناصر جديدة.

النقاط الرئيسية

  • body هي خاصية محسوبة إلزامية لجميع الأنواع التي تطبق بروتوكول View
  • some View هو نوع إرجاع غير شفاف يسمح لـ SwiftUI بتحسين العرض
  • body تُستدعى عند كل تغيير في الحالة ولكن يجب ألا يكون لها آثار جانبية
  • ViewBuilder يلف body ضمنياً إذا أعادت عناصر متعددة
  • body لا تُستدعى إذا لم تتغير هوية وحالة View

ما هو body في SwiftUI؟

body هي خاصية محسوبة تمثل المتطلب الإلزامي الوحيد لبروتوكول View. كل بنية تطابق View يجب أن تنفذ body. تعيد الخاصية المحتوى الذي يعرضه SwiftUI على الشاشة — يمكن أن يكون نصاً أو صورة أو زراً أو حاوية بعناصر متداخلة أو أي نوع آخر يطابق بروتوكول View.

توقيع body ثابت دائماً: var body: some View { get }. نوع الإرجاع هو some View (نوع غير شفاف)، وليس نوعاً محدداً. هذا يعني أن Views مختلفة قد تعيد أنواعاً محددة مختلفة في body، لكن مترجم Swift يثبت النوع المحدد لكل تنفيذ في وقت الترجمة.

وفقاً WWDC 2022، body هي نقطة الدخول لوصف الواجهة التصريحي. على عكس UIKit، حيث تقوم بإنشاء وتكوين UIView بشكل أمري، في SwiftUI تصف بشكل تصريحي ما يجب عرضه، و SwiftUI نفسه يحسب كيفية تنفيذه.

body كدالة نقية

يجب أن تتصرف body كـ دالة نقية — مع نفس المدخلات (خصائص البنية والحالة) يجب أن تعيد نفس شجرة View. إذا كانت body تعتمد على حالة خارجية قابلة للتغيير (متغيرات عامة، UserDefaults بدون غلاف @AppStorage)، يصبح السلوك غير متوقع وقد يعيد SwiftUI رسم الشاشة بشكل غير صحيح.

كيف تعمل الخاصية المحسوبة body

الخاصية المحسوبة body لا تخزن قيمة — يتم حسابها في كل مرة يتم الوصول إليها. عندما يحدد SwiftUI أن الحالة تغيرت، يعيد بناء بنية View ويقرأ القيمة الجديدة لـ body للحصول على شجرة العناصر الحالية للعرض.

swift
struct CounterView: View {
    @State private var count = 0

    var body: some View {
        VStack {
            Text("العداد: \(count)")
                .font(.largeTitle)
            Button("زيادة") {
                count += 1
            }
            .padding()
            .background(.blue)
            .foregroundColor(.white)
            .cornerRadius(8)
        }
    }
}

في هذا المثال، body تعيد VStack يحتوي على نص وزر مع معدلات. عند الضغط على الزر، تزداد خاصية @State count، يعيد SwiftUI بناء بنية CounterView ويستدعي body مرة أخرى للحصول على الشجرة المحدثة بقيمة النص الجديدة.

المعدلات (.font, .padding, .background, .foregroundColor, .cornerRadius) لا تعدل View الأصلية بل تلفها في ModifiedContent — نوع جديد يضيف التعديل. كل معدل ينشئ مستوى تداخل آخر، وهو أمر مهم لمراعاة الأداء.

body والنوع غير الشفاف some View

some View في نوع إرجاع body ليس مجرد اتفاقية بل متطلب من المترجم. يتطلب Swift أن جميع مسارات الإرجاع في body لها نفس النوع المحدد. بدون @ViewBuilder لا يمكنك إرجاع Text في فرع و Button في فرع آخر — سينتج المترجم خطأ.

swift
struct ConditionalView: View {
    var isReady: Bool

    @ViewBuilder
    var body: some View {
        if isReady {
            Text("جاهز")
                .foregroundColor(.green)
        } else {
            ProgressView()
        }
    }
}

@ViewBuilder على body يسمح باستخدام المنطق الشرطي (if/else, switch) بدون أخطاء ترجمة. ViewBuilder يلف تلقائياً الفروع المختلفة في ConditionalContent — نوع خاص يخفي اختلافات الأنواع المحددة. هذه قدرة أساسية لبناء واجهات ديناميكية.

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

أداء some View

استخدام some View بدلاً من نوع محدد لا يقلل الأداء — المترجم يعرف النوع الدقيق في وقت الترجمة وينشئ كوداً مباشراً بدون إرسال ديناميكي. AnyView، على العكس، يستخدم محو النوع (type erasure) مع الحمل الزائد للالتفاف في حاوية وجودية.

دورة حياة body: متى وكيف تُستدعى

body تُستدعى بواسطة SwiftUI في ثلاثة سيناريوهات رئيسية: عند عرض View لأول مرة، عند تغيير @State/@Binding/@ObservedObject/@StateObject، وعندما تمرر View الأم قيماً جديدة عبر المُهيئ. قد يستدعي SwiftUI أيضاً body عند تغيير قيم البيئة (@Environment).

تكرار استدعاءات body لا ينبغي أن يقلقك — SwiftUI يحسن إعادة الرسم من خلال آلية الهوية. كل View في التسلسل الهرمي لها معرف فريد. إذا لم تتغير الهوية وبيانات الإدخال — لا تُستدعى body حتى إذا أعيد رسم View الأم. يتم تحقيق ذلك من خلال مقارنة Equatable والاستقرار البنيوي.

swift
struct ParentView: View {
    var body: some View {
        ChildView(name: "Alice") // هوية مستقرة
    }
}

struct ChildView: View {
    let name: String
    var body: some View {
        Text("مرحباً، \(name)!")
    }
}

في هذا المثال، إذا أعيد رسم ParentView لكنه يمرر نفس قيمة name — لا يُستدعى ChildView.body. يقارن SwiftUI بيانات إدخال البنية، وإذا لم تتغير، يتخطى إعادة رسم المكون الابن. هذه هي آلية تفريق المشاهدات.

عندما تُستدعى body بشكل غير متوقع

هناك عدة مخاطر تؤدي إلى استدعاءات غير متوقعة لـ body: استخدام كلاسات بدون ObservableObject، تمرير closures تم إنشاؤها داخل body (كل إنشاء closure يعطي هوية جديدة)، والاستخدام غير الصحيح لـ EquatableView. إذا كانت body تُستدعى كثيراً — تحقق من استقرار هوية جميع المكونات الابنة.

أفضل الممارسات للعمل مع body

القاعدة الأولى: يجب أن تكون body ضئيلة. انقل المنطق المعقد إلى خصائص محسوبة أو طرق منفصلة تعيد View. هذا يحسن قابلية القراءة ويسمح لـ SwiftUI بتحديد أجزاء التسلسل الهرمي التي تغيرت بدقة أكبر. قسم الـ bodies الكبيرة إلى مكونات فرعية بحدود مسؤولية واضحة.

القاعدة الثانية: لا تستخدم body لتنفيذ العمل. تحميل البيانات، عمليات الشبكة، الكتابة في قاعدة البيانات — كل هذا يجب أن يحدث خارج body، في المهام (tasks) أو معدّلات onChange أو من خلال ObservableObject. body مخصص فقط للإعلان عن الواجهة.

القاعدة الثالثة: استخدم خاصية EquatableView أو بروتوكول Equatable مخصص لـ Views إذا كانت المقارنة البنيوية القياسية غير كافية. هذا يسمح لك بإخبار SwiftUI صراحةً متى يحتاج المكون الابن إلى إعادة رسم وتجنب استدعاءات body غير الضرورية.

القاعدة الرابعة: إذا كانت body تحتوي على حسابات معقدة (تنسيق، تصفية، فرز) — استخدم @State للتخزين المؤقت للنتيجة أو انقل الحسابات إلى طريقة منفصلة تُستدعى من onChange. الحسابات المتكررة في body مع كل تحديث للحالة هي سبب شائع لبطء الرسوم المتحركة.

القاعدة الخامسة: للقوائم (List, ForEach) تأكد من المعرفات المستقرة عبر المعامل id. بدون هوية مستقرة، يعيد ForEach إنشاء جميع العناصر عند أي تغيير، مستدعياً body لكل منها، حتى لو تغير عنصر واحد فقط.

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

ما هو body في SwiftUI؟

body هي الخاصية المحسوبة لبروتوكول View التي تعيد المحتوى للعرض. إنها المتطلب الإلزامي الوحيد للبروتوكول. نوع الإرجاع هو some View، مما يسمح لـ SwiftUI بتحسين التسلسل الهرمي في وقت الترجمة.

هل يمكن استدعاء body عدة مرات؟

نعم، يستدعي SwiftUI body عند كل تغيير في الحالة (@State, @Binding, @ObservedObject) أو بيانات الإدخال. هذا سلوك طبيعي لإطار عمل تصريحي. يحسن SwiftUI تكرار الاستدعاءات من خلال آلية الهوية ومقارنة Equatable.

لماذا تعيد body some View بدلاً من نوع محدد؟

some View هو نوع غير شفاف يخفي التنفيذ المحدد. يثبت المترجم النوع في وقت الترجمة، مما يضمن أداء الاستدعاء المباشر. هذا يوفر مرونة: يمكنك تغيير نوع الإرجاع دون تغيير التوقيع.

هل يمكن إرجاع nil من body؟

لا، body لا يمكن أن تكون اختيارية — نوع الإرجاع some View لا يسمح بـ nil. إذا كنت بحاجة لإخفاء عنصر بشكل شرطي، استخدم المنطق الشرطي داخل @ViewBuilder أو أعد EmptyView، الذي لا يشغل مساحة في التسلسل الهرمي.

هل يؤثر عدد المعدلات على أداء body؟

كل معدل ينشئ طبقة ModifiedContent جديدة، مما يزيد عمق التسلسل الهرمي. لمعظم الشاشات (حتى 50 معدلاً) التأثير ضئيل. العدد المفرط من المعدلات (مئات) قد يبطئ عملية المقارنة. جمّع المعدلات المرتبطة في امتدادات مخصصة.

الخلاصة

  • body هي الخاصية المحسوبة الإلزامية لبروتوكول View التي تحدد محتوى الشاشة
  • some View هو نوع إرجاع غير شفاف يخفي التنفيذ المحدد عن الكود المستدعي
  • @ViewBuilder يُطبق ضمنياً على body لدعم المنطق الشرطي والعناصر المتعددة
  • body يجب ألا تحتوي على آثار جانبية — إنها إعلان واجهة نقي
  • SwiftUI يحسن استدعاءات body من خلال آلية الهوية ومقارنة Equatable
  • قسّم الـ bodies الكبيرة إلى مكونات فرعية لأداء وقراءة أفضل
  • AnyView يزيد الحمل الزائد — استخدم @ViewBuilder و Group بدلاً من محو النوع

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

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

اقرأ أيضًا