@State — ما هو، الغرض والاستخدام في SwiftUI

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

@State هو Property Wrapper في SwiftUI لإدارة الحالة المحلية داخل عرض واحد. يعيد SwiftUI رسم العرض تلقائياً كلما تغيرت خاصية @State، مما يجعل الواجهة تفاعلية دون استدعاءات تحديث يدوية. وفقاً لوثائق Apple Developer (2025)، يُوصى باستخدام @State للأنواع والهياكل البسيطة التي تنتمي إلى عرض واحد. @State هو أبسط طريقة لإضافة التفاعلية إلى واجهة SwiftUI.

الخلاصة

  • @State — Property Wrapper للحالة المحلية التابعة لعرض واحد
  • التحديث التلقائي — يعيد SwiftUI استدعاء body عند تغيير خاصية @State
  • الأنواع البسيطة — @State يعمل مع String وInt وBool وenum والهياكل
  • لا تمرره إلى العروض الفرعية — استخدم @Binding للتغييرات من المكونات الفرعية
  • private — تُعلن خصائص @State دائماً مع المُعدِّل private

ما هو @State في SwiftUI؟

@State هو Property Wrapper مدمج في SwiftUI يسمح للعرض بتخزين وتتبع حالته الخاصة. عندما تتغير قيمة @State، يعيد SwiftUI رسم العرض تلقائياً عن طريق استدعاء خاصية body مرة أخرى. هذا هو أساس البرمجة التفاعلية في SwiftUI: يعلن المطور عن الحالة، ويتولى الإطار مزامنة الواجهة.

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

قيود مهمة: @State مخصص فقط لـ أنواع القيمة (الهياكل، التعدادات، الأنواع البدائية). للأنواع المرجعية (الفئات)، استخدم @StateObject أو @ObservedObject. إذا أسندت فئة إلى خاصية @State، لن يتمكن SwiftUI من اكتشاف التغييرات داخل الكائن — فقط استبدال المرجع بالكامل.

كيف يعمل @State تحت الغطاء؟

ينفذ SwiftUI @State من خلال آلية التخزين الداخلية. تحصل كل خاصية @State على خلية ذاكرة مخصصة مخزنة في حاوية تخزين خاصة بالعرض. عندما يحدث كتابة إلى wrappedValue، يُعلم SwiftUI رسم التبعيات الخاص به عبر didSet بضرورة إعادة الرسم.

swift
struct ContentView: View {
    @State private var name: String = "User"
    @State private var isLoggedIn: Bool = false

    var body: some View {
        VStack {
            Text("مرحباً، \(name)")
            Button(isLoggedIn ? "تسجيل الخروج" : "تسجيل الدخول") {
                isLoggedIn.toggle()
            }
        }
    }
}

في المثال، هناك خاصيتان @State: name (String) و isLoggedIn (Bool). عند استدعاء isLoggedIn.toggle()، يضع SwiftUI علامة على ContentView بأنها تحتاج إلى تحديث ويعيد استدعاء body في دورة العرض التالية. النقطة الأساسية: تُعلن خصائص @State دائماً مع المُعدِّل private — وهذا يشير إلى أن الحالة تنتمي حصرياً إلى العرض الحالي ولا يجب تغييرها من الخارج مباشرة.

لمراقبة التغييرات، يستخدم SwiftUI CurrentValueSubject من Combine. تنشئ كل خاصية @State ناشراً مخفياً يُعلم النظام بكل تغيير. يسمح هذا لـ SwiftUI بإعادة رسم الحد الأدنى الضروري فقط من العروض، متجنباً التحديث الكامل للتسلسل الهرمي.

متى تستخدم @State في المشروع

@State مثالي لـ الحالات المحلية البسيطة: حقول النص في البحث، المؤشرات المنطقية للنوافذ المشروطة، مفاتيح الإعدادات، العدادات، عناصر القائمة المحددة. إذا كانت القيمة تُستخدم فقط في عرض واحد ومكوناته الفرعية (عبر @Binding)، فإن @State هو الخيار الصحيح. للحالات التي يجب أن تبقى بعد إغلاق العرض (مثل بيانات النموذج)، يعمل @State أيضاً طالما بقي العرض في التسلسل الهرمي.

  • حقول النص — @State لتخزين النص المُدخل في TextField
  • المؤشرات المنطقية — @State لإظهار/إخفاء النوافذ المشروطة والورقات
  • اختيار العناصر — @State لتتبع التبويب أو الصف المحدد
  • العدادات — @State للقيم الرقمية مع الزيادة/النقصان
  • الحسابات الوسيطة — @State لتخزين مؤقت للنتائج داخل العرض

لا تستخدم @State لـ الحالات العامة للتطبيق، أو تخزين بيانات الشبكة مؤقتاً، أو الكائنات المستخدمة عبر شاشات متعددة. @StateObject و @EnvironmentObject مصممان لهذه الأغراض. كما أن @State غير مناسب لتخزين كميات كبيرة من البيانات — كل تغيير سيعيد رسم العرض بأكمله.

@State و @Binding: العمل معاً

@Binding هو جسر بين @State في العرض الأب والعرض الابن الذي يحتاج إلى تعديل تلك الحالة. يعلن الأب عن @State، ويتلقى المكون الابن Binding عبر إسقاط $. تغيير Binding في العرض الابن يُحدث تلقائياً @State في الأب — والعكس صحيح. يضمن ذلك تدفق بيانات أحادي الاتجاه مع إمكانية التغذية الراجعة.

swift
struct ParentView: View {
    @State private var text: String = ""

    var body: some View {
        ChildView(text: $text)
    }
}

struct ChildView: View {
    @Binding var text: String

    var body: some View {
        TextField("Enter text", text: $text)
    }
}

في القائمة، ParentView يمتلك @State text، ويتلقى ChildView $text كـ Binding. يرتبط TextField داخل ChildView بهذا Binding عبر text: $text. عندما يكتب المستخدم في TextField، تتغير القيمة في ChildView عبر Binding، مما يتسبب في تحديث @State في ParentView. يتم إعادة رسم كلا العرضين بالقيمة الجديدة.

الأخطاء الشائعة عند العمل مع @State

الخطأ الأكثر شيوعاً هو إسناد فئة إلى خاصية @State. إذا كتبت @State var model = MyClass()، لن يتمكن SwiftUI من تتبع التغييرات في خصائص الفئة — فقط استبدال الكائن. للفئات، استخدم دائماً @StateObject. المشكلة الثانية الشائعة هي إعلان @State بدون المُعدِّل private، مما ينتهك مبدأ تغليف الحالة.

تمرير @State مباشرة إلى عرض ابن بدون $ هو خطأ نموذجي آخر. إذا مررت TextField(text: text) بدلاً من TextField(text: $text)، يتلقى المكون الابن سلسلة نصية فقط، وليس Binding. لن تتم مزامنة تغييرات النص في TextField مع @State للأب. استخدم دائماً إسقاط $ لتمرير Binding.

الخطأ الثالث هو خصائص @State متعددة للبيانات المرتبطة. إذا كانت عدة قيم تشكل كلاً واحداً منطقياً (مثل حقول النموذج)، ادمجها في هيكل واحد مع @State واحد. هذا يبسط تمرير الحالة إلى العروض الفرعية ويقلل عدد مشغلات التحديث الفردية.

أمثلة على استخدام @State في SwiftUI

@State يُستخدم في معظم مشاريع SwiftUI للتفاعل الأساسي. لنأخذ مثال نموذج تسجيل الدخول حيث يدير @State حقول النص وحالة التحميل. يظهر هذا النمط في كل تطبيق — من الملاحظات البسيطة إلى الحلول المؤسسية المعقدة.

swift
struct LoginView: View {
    @State private var email: String = ""
    @State private var password: String = ""
    @State private var isLoading: Bool = false
    @State private var errorMessage: String?

    var body: some View {
        Form {
            TextField("Email", text: $email)
            SecureField("Password", text: $password)
            Button("تسجيل الدخول") {
                login()
            }.disabled(isLoading)
        }
    }

    private func login() {
        isLoading = true
        // تنفيذ طلب الشبكة
    }
}

في المثال، هناك أربع خصائص @State: email و password لحقول النموذج، و isLoading للإشارة إلى التحميل، و errorMessage لعرض الأخطاء. كل خاصية تدير بشكل مستقل جزءها من الواجهة. عندما يتغير isLoading، يتم تعطيل الزر تلقائياً عبر disabled(isLoading) — دون تحديث يدوي للواجهة.

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

لماذا يُعلن @State مع private؟

@State مصمم للحالة المحلية لعرض معين. المُعدِّل private يضمن أن المكونات الأخرى لا يمكنها تغييره مباشرة، مما يكسر التغليف. للوصول الخارجي، استخدم إسقاط $.

هل يمكن أن يحتوي @State على مصفوفة أو قاموس؟

نعم، @State يدعم المصفوفات والقواميس لأنها أنواع قيمة. ومع ذلك، عندما يتغير عنصر في مصفوفة، يعيد SwiftUI رسم العرض بأكمله. للقوائم الكبيرة، @StateObject مع @Published أكثر كفاءة.

ماذا يحدث عند تعيين nil لخاصية @State بنوع Optional؟

@State يعمل بشكل صحيح مع الأنواع الاختيارية. عند تعيين nil، يكتشف SwiftUI التغيير ويعيد رسم العرض. هذا مناسب لحالات مثل errorMessage: String؟، حيث nil يعني عدم وجود خطأ.

كيف يتصرف @State عند إعادة ظهور العرض؟

@State يحتفظ بالقيمة طالما بقي العرض في التسلسل الهرمي. إذا تمت إزالة العرض من التسلسل الهرمي وإضافته مرة أخرى، يُعاد تهيئة @State بالقيمة الافتراضية. للاستمرارية، استخدم @AppStorage.

هل يمكن تحريك تغييرات @State؟

نعم، لف التغيير في withAnimation: withAnimation(.easeInOut) { isExpanded.toggle() }. يقوم SwiftUI بتحريك الانتقال بين الحالة القديمة والجديدة للواجهة مع نوع الحركة المحدد.

الملخص

  • @State — Property Wrapper للحالة المحلية لعرض واحد، يقوم بتحديث الواجهة تلقائياً
  • يعمل مع الأنواع البسيطة: String وInt وBool وكذلك الهياكل والتعدادات
  • لا يعمل مع الأنواع المرجعية (الفئات) — استخدم @StateObject
  • دائماً private — لا يجب تغيير الحالة من الخارج مباشرة
  • إسقاط $ — ينشئ Binding لتمرير حقوق التغيير إلى العروض الفرعية
  • عدة @State في عرض واحد — ممارسة طبيعية للحالات المستقلة
  • withAnimation — يسمح بتحريك تغييرات خصائص @State

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

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

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

اقرأ أيضًا