.onDisappear: مبدأ العمل والاستخدام في SwiftUI

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

.onDisappear هو معدّل SwiftUI ينفذ إغلاقًا عند إزالة View من تسلسل الواجهة. يحدث الاستدعاء عند إغلاق الشاشة، أو تبديل التبويب، أو إلغاء النافذة المشروطة، أو تمرير عنصر خارج نطاق الرؤية. وفقًا لوثائق مطوري Apple (2026)، لا يضمن onDisappear الاستدعاء في سيناريوهات التعطل أو عند إنهاء التطبيق بواسطة مراقب النظام. اقرأ المزيد عن SwiftUI في مقال SwiftUI.

الخلاصة

  • .onDisappear — معدّل SwiftUI لتنفيذ الكود عند اختفاء View.
  • التناظر — onDisappear مزدوج مع onAppear ويُستدعى بترتيب عكسي.
  • تنظيف الموارد — السيناريو الرئيسي: إلغاء المؤقتات، إغلاق الاتصالات، حفظ الحالة.
  • التحليلات — عبر onDisappear تُرسل أحداث إغلاق الشاشة ووقت المكوث وإنهاء الجلسة.
  • عدم وجود ضمان — لا يُستدعى onDisappear عند تعطل التطبيق.

ما هو .onDisappear؟

.onDisappear هو معدّل View في SwiftUI يأخذ إغلاقًا Void وينفذه عند إزالة View من التسلسل. مع .onAppear يُشكل دورة الحياة الكاملة للشاشة: الظهور — العمل — الاختفاء. قدمت Apple onDisappear مع إصدار SwiftUI في iOS 13 كبديل لـ viewDidDisappear من UIKit.

من الناحية التركيبية، .onDisappear مطابق لـ onAppear: يعدّل أي View ويُرفق إغلاقًا يستدعيه محرك عرض SwiftUI عند إزالة العرض. على عكس UIKit حيث يتم استدعاء viewDidDisappear فقط بعد اكتمال رسم الانتقال، يمكن استدعاء onDisappear في SwiftUI قبل انتهاء الرسم — في اللحظة التي يتم فيها وضع علامة على View للحذف.

بناء جملة onDisappear

بناء الجملة الأساسي لـ onDisappear موجز مثل onAppear. لا يقبل المعدّل معاملات إضافية — فقط الإغلاق الذي ينفذ بشكل متزامن في الخيط الرئيسي.

swift
struct DetailView: View {
    var body: some View {
        Text("Detail Screen")
            .onDisappear {
                print("اختفت DetailView من الشاشة")
            }
    }
}

كيف يعمل .onDisappear

آلية الاستدعاء لـ onDisappear معاكسة لـ onAppear: العناصر الفرعية تستقبل onDisappear أولاً، ثم الأصل. تضمن هذه القاعدة child-first تحرير الموارد الفرعية قبل تحرير موارد الأصل. إذا كان العنصر الفرعي يعتمد على بيانات الأصل، يجب أن يكون قادرًا على الإنهاء بشكل صحيح دون سياق الأصل.

SwiftUI يستدعي onDisappear عندما تتم إزالة View من رسم البيان. المشغلات تشمل: pop من NavigationStack، تبديل TabView، إلغاء المشروط (sheet/fullScreenCover)، أو View معروض بشكل شرطي (if/switch). في List و ScrollView، يُستدعى onDisappear عندما يتم تمرير خلية خارج مخزن التحميل المسبق.

ترتيب الاستدعاء child-first

ترتيب child-first يعني أنه إذا كان VStack يحتوي على ثلاث View فرعية، فإن onDisappear يُستدعى أولاً لكل فرعي بترتيب عكسي، ثم للأصل. هذا ضروري للتنظيف الصحيح: يتم إلغاء المؤقتات الفرعية قبل أن يحرر ViewModel الأصل الموارد المشتركة.

swift
struct ParentView: View {
    var body: some View {
        VStack {
            ChildView(id: "A")
            ChildView(id: "B")
        }
        .onDisappear {
            print("Parent onDisappear — الأخير")
        }
    }
}

struct ChildView: View {
    let id: String
    var body: some View {
        Text("Child \(id)")
            .onDisappear {
                print("Child \(id) onDisappear")
            }
    }
}

مخرجات الكونسول: Child B onDisappear، Child A onDisappear، Parent onDisappear — الأخير. الترتيب العكسي مقارنة بـ onAppear يضمن سلسلة تنظيف صحيحة.

متى يُستدعى .onDisappear

سيناريوهات الاستدعاء لـ onDisappear تعتمد على نوع الحاوية. في NavigationStack، يتم تفعيل onDisappear عند pop-to-root أو pop-back العادي أو إلغاء الشاشة بالسحب (interactivePopGestureRecognizer). في TabView، يؤدي تبديل التبويب إلى استدعاء onDisappear للتبويب المخفي و onAppear للظاهر — كلا المعدّلين يتم تفعيلهما بشكل متزامن تقريبًا.

في Sheet و fullScreenCover، يُستدعى onDisappear عند الإلغاء البرمجي (عبر @Environment(\.dismiss)) أو إيماءة السحب لأسفل. تفاصيل مهمة: إذا تم فتح sheet لكن المستخدم بدّل التطبيق، فإن onDisappear لا يُستدعى حتى الإغلاق الفعلي.

السيناريواستدعاء onDisappearملاحظة
Pop في NavigationStackنعممباشرة بعد الرسم
تبديل تبويب TabViewنعمالتبويب الحالي
إلغاء sheetنعمقبل اكتمال الرسم
التمرير في Listنعمالخلية خرجت من منطقة التحميل المسبق
تصغير التطبيقلالا يوجد ضمان للاستدعاء
تعطل/watchdog killلالا يُستدعى

أمثلة .onDisappear

حالات الاستخدام الرئيسية لـ onDisappear هي تنظيف الموارد وحفظ الحالة والتتبع. على عكس onAppear، تُنفذ مهام onDisappear عند الخروج ولا تحتاج إلى فحص التكرار لأن View تختفي مرة واحدة.

حفظ الحالة وإلغاء المؤقتات

حفظ الحالة في onDisappear مفيد بشكل خاص للنماذج حيث يجب حفظ البيانات عند مغادرة الشاشة. يتم إلغاء المؤقتات واشتراكات Combine في onDisappear لمنع التسرب عند العودة إلى الشاشة.

swift
struct FormView: View {
    @State private var draftText = ""
    @State private var timer: Timer?
    
    var body: some View {
        TextField("Enter text", text: $draftText)
            .onAppear {
                timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in
                    saveDraft()
                }
            }
            .onDisappear {
                timer?.invalidate()
                timer = nil
                saveDraft()
            }
    }
    
    private func saveDraft() {
        UserDefaults.standard.set(draftText, forKey: "draft")
    }
}

إلغاء المؤقت في onDisappear يمنع تنفيذ الكود بعد إغلاق الشاشة بالفعل. بدون الإلغاء، قد يحاول المؤقت تحديث @State الذي لم يعد ينتمي إلى View الحالية، مما يسبب تحذير وقت التشغيل.

تتبع الوقت على الشاشة

مدة المكوث على الشاشة هي سيناريو تتبع كلاسيكي. سجل الوقت في onAppear، احسب الفرق في onDisappear وأرسل حدث تحليلي بمدة الجلسة.

swift
struct TrackedView: View {
    @State private var appearTime: Date?
    
    var body: some View {
        Text("Tracked Screen")
            .onAppear {
                appearTime = Date()
                Analytics.shared.logEvent("screen_view", params: ["screen": "TrackedView"])
            }
            .onDisappear {
                if let start = appearTime {
                    let duration = Date().timeIntervalSince(start)
                    Analytics.shared.logEvent("screen_close", params: [
                        "screen": "TrackedView",
                        "duration_ms": Int(duration * 1000)
                    ])
                }
            }
    }
}

.onDisappear مقابل .onAppear — الفروقات

الفرق الرئيسي بين onDisappear و onAppear هو ترتيب الاستدعاء وضمانات التنفيذ. onAppear يُستدعى عند إضافة View إلى التسلسل ويمكن استدعاؤه مرة أخرى عند إعادة الإنشاء. onDisappear يُستدعى عند الإزالة ويضمن تفعيله فقط عند الإغلاق العادي، وليس في سيناريوهات التعطل.

وفقًا لمؤتمر WWDC 2024، توصي Apple باعتبار onDisappear نقطة تنظيف وليس نقطة تأكيد بيانات. يجب حفظ البيانات المهمة (المدفوعات، التسجيلات) في الوقت الفعلي، وليس في لحظة اختفاء View، لأن onDisappear غير مضمون عند تصغير التطبيق.

الخاصية.onAppear.onDisappear
لحظة الاستدعاءإضافة View للتسلسلإزالة View من التسلسل
الترتيبParent-firstChild-first
الضمانعاليليس عند التعطل
المهمة الرئيسيةالتهيئةالتنظيف
الاستدعاء المتكررعند إعادة إنشاء Viewمرة واحدة لكل اختفاء

توصية: استخدم onDisappear فقط للتنظيف غير الحرج والتتبع. لحفظ البيانات، استخدم scenePhase أو إشعارات applicationWillTerminate في AppDelegate.

سيناريوهات الاستخدام النموذجية

السيناريو 1: تشغيل شبيه بـ YouTube. على شاشة تفاصيل الفيديو، يحفظ onDisappear موضع التشغيل في UserDefaults. عند إعادة الفتح، يستعيد onAppear الموضع من UserDefaults مما يخلق تجربة مشاهدة مستمرة.

السيناريو 2: إلغاء اشتراك Combine. إذا كان ViewModel يستخدم ناشري Combine، يلغي onDisappear الاشتراك عبر cancellable?.cancel(). هذا يمنع تحديثات UI بعد مغادرة الشاشة، وهو مهم بشكل خاص للقوائم ذات الترقيم الصفحي واستعلامات البحث.

السيناريو 3: إغلاق WebSocket. في التطبيقات ذات الاتصالات الفورية (المراسلات، خلاصات الأسعار)، يغلق onDisappear اتصال WebSocket لتوفير البطارية والبيانات. تتم إعادة الاتصال في onAppear عند العودة إلى الشاشة.

إغلاق WebSocket في onDisappear

WebSocket هو مورد نموذجي يجب إغلاقه عند مغادرة الشاشة. في المثال أدناه، يفصل onDisappear الاتصال، ويعيد onAppear الاتصال، مما يوفر توفيرًا كبيرًا في البيانات للتطبيقات ذات الشاشات المتعددة.

swift
struct ChatView: View {
    @StateObject private var socket = WebSocketManager()
    
    var body: some View {
        ChatListView(messages: socket.messages)
            .onAppear {
                socket.connect()
            }
            .onDisappear {
                socket.disconnect()
            }
    }
}

مهم: عند التبديل بين تبويبات TabView، يتم تفعيل onDisappear للتبويب الحالي و onAppear للتبويب التالي بشكل متزامن تقريبًا. بالنسبة لـ WebSocket، قد يتسبب هذا في دورة فصل-إعادة اتصال تخلق حملاً غير ضروري. الحل هو استخدام تأخير أو التحقق مما إذا كان الاتصال مطلوبًا على الشاشة التالية.

الأسئلة المتداولة

هل يمكن ألا يتم استدعاء .onDisappear؟

نعم، لا يضمن .onDisappear الاستدعاء عند تعطل التطبيق (crash، watchdog kill) أو تصغيره دون إغلاق الشاشة أو السيناريوهات النظامية حيث يُنهى التطبيق في الخلفية. للبيانات المهمة، استخدم scenePhase أو applicationWillTerminate.

كيف نفرق بين .onDisappear و .scenePhase؟

.onDisappear يُفعّل عند إزالة View من التسلسل — استدعاء محلي لشاشة معينة. .scenePhase (عبر @Environment(\.scenePhase)) يُفعّل عند تغير حالة التطبيق بالكامل: active، inactive، background. استخدم onAppear+onDisappear لتتبع الوقت على الشاشة و scenePhase لحفظ الحالة العامة.

لماذا يتم استدعاء .onDisappear عدة مرات متتالية؟

السبب هو التبديل السريع بين التبويبات أو push/pop المتكرر لنفس الشاشة. قد ينشئ SwiftUI مثيل View جديد، ويزيل القديم، وينشئ مرة أخرى — في كل مرة يستدعي onDisappear و onAppear. تحقق مما إذا كنت تستخدم .id() أو .equatable() أو تعيد إنشاء View في body الأصل.

هل يعمل .onDisappear في SwiftUI على macOS؟

نعم، .onDisappear مدعوم بالكامل على macOS (10.15+) بنفس السلوك: يُستدعى عند إغلاق النافذة أو إزالة لوحة split view أو إلغاء المشروط. على macOS، يُستدعى onDisappear أيضًا عند إخفاء النافذة (وليس فقط عند الإغلاق)، وهو أمر مهم يجب مراعاته لتطبيقات macOS.

كيف نلغي URLSessionTask في .onDisappear؟

احفظ مرجعًا لـ URLSessionTask في @State واستدعِ task.cancel() في onDisappear. بدلاً من ذلك، استخدم المعدّل .task الذي يلغي العملية غير المتزامنة تلقائيًا عند اختفاء View. يُفضل .task لجميع العمليات غير المتزامنة بما في ذلك URLSession.

الملخص

  • .onDisappear — معدّل مزدوج لـ onAppear لتنظيف الموارد والتتبع عند اختفاء View.
  • ترتيب child-first — View الفرعية تستقبل onDisappear قبل الأصل لسلسلة تنظيف صحيحة.
  • لا ضمان للاستدعاء — عند التعطل أو التصغير أو إنهاء النظام، قد لا يتم تفعيل onDisappear.
  • حالات الاستخدام الرئيسية — إلغاء المؤقتات، إغلاق WebSocket، حفظ الحالة، تتبع المدة.
  • التحليلات — onDisappear يرسل أحداث screen_close مع وقت المكوث المحسوب عبر Date().timeIntervalSince.
  • للعمليات غير المتزامنة — استخدم .task بدلاً من onDisappear مع Task {} لأن .task يلغي الطلبات تلقائيًا.

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

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

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

اقرأ أيضًا