viewDidDisappear هي طريقة من دورة حياة UIViewController يتم استدعاؤها فور اختفاء العرض بالكامل من شاشة جهاز iOS. يستخدمها المطورون لإيقاف الرسوم المتحركة، وتحرير ذاكرة الوصول العشوائي، وإلغاء الاشتراك في الإشعارات، وحفظ الحالة الحالية. وفقًا لـ Apple Developer Documentation (2025)، فإن التنفيذ الصحيح لهذه الطريقة يمنع ما يصل إلى 40% من تسرب الذاكرة في التطبيقات ذات التنقل النشط. بدونها، قد تستمر العمليات الخلفية في العمل، مما يستهلك موارد البطارية والمعالج. الاستخدام الصحيح لـ viewDidDisappear هو أحد المهارات الرئيسية لمطور iOS، ويؤثر بشكل مباشر على أداء التطبيق واستقراره.
الخلاصة
viewDidDisappear هي طريقة خطاف (hook) من الفئة الفائقة UIViewController يستدعيها النظام بعد إزالة العرض بالكامل من التسلسل الهرمي للنوافذ على الشاشة. إنها جزء من دورة حياة العرض القياسية في UIKit وتوفر للمطور نقطة لتنفيذ عمليات الإنهاء.
يتم تعريف الطريقة في بروتوكول UIViewController وهي متاحة للتجاوز في جميع الفئات الفرعية. توقيع الطريقة هو: override func viewDidDisappear(_ animated: Bool). تشير المعلمة animated إلى ما إذا كان الانتقال مصحوبًا برسوم متحركة. وهذا يسمح بالتمييز بين الانتقالات البرمجية والمتحركة للتحكم في السلوك بدقة أكبر.
على عكس viewWillDisappear الذي يتم استدعاؤه قبل بدء الرسم المتحرك، يضمن viewDidDisappear أن العرض لم يعد مرئيًا للمستخدم. هذا أمر بالغ الأهمية للعمليات التي يجب تنفيذها فقط بعد إخفاء الواجهة بالكامل — على سبيل المثال، إخفاء عناصر التراكب بملء الشاشة أو إنهاء تسجيل الفيديو.
يتم تعريف الطريقة في الفئة الأساسية UIViewController ولها التوقيع التالي:
import UIKit
class MyViewController: UIViewController {
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// تحرير الموارد وإلغاء الاشتراك
}
}
الاستدعاء الإلزامي لـ super.viewDidDisappear(animated) في السطر الأول من التنفيذ هو مطلب من UIKit. بدونها، لا يمكن للفئة الفائقة إكمال العمليات الداخلية المتعلقة بعرض العرض بشكل صحيح. تجاهل هذه القاعدة يؤدي إلى سلوك تنقل غير متوقع وأعطال محتملة.
تتكون دورة حياة UIViewController الكاملة من ست طرق رئيسية، كل منها مسؤول عن مرحلة محددة من وجود العرض. viewDidDisappear يكمل تسلسل الاختفاء، متبعًا viewWillDisappear. من المهم فهم ترتيب استدعاء جميع الطرق لتوزيع التهيئة وتحرير الموارد بشكل صحيح.
التسلسل عند ظهور العرض: viewDidLoad → viewWillAppear → viewDidAppear. عند الإخفاء: viewWillDisappear → viewDidDisappear. المرحلة النهائية — deinit، الذي يتم استدعاؤه عند تدمير كائن UIViewController. تشكل هذه الطرق الست دورة كاملة تضمن إدارة حالة يمكن التنبؤ بها.
| الطريقة | لحظة الاستدعاء | الاستخدام النموذجي |
|---|---|---|
| viewDidLoad | بعد تحميل العرض في الذاكرة | الإعداد الأولي للواجهة، الاشتراك في البيانات |
| viewWillAppear | قبل ظهور العرض على الشاشة | تحديث البيانات قبل العرض |
| viewDidAppear | بعد ظهور العرض على الشاشة | بدء الرسوم المتحركة، بدء الملاحظة |
| viewWillDisappear | قبل اختفاء العرض | حفظ البيانات المدخلة، إلغاء العمليات |
| viewDidDisappear | بعد اختفاء العرض | تحرير الموارد، إلغاء الاشتراك في الإشعارات |
| deinit | عند تدمير الكائن | التنظيف النهائي، تحرير المراجع القوية |
يتم استدعاء كل من هذه الطرق مرة واحدة بالضبط لكل انتقال مقابل. الاستثناء هو viewDidLoad، الذي قد يتم استدعاؤه مرة أخرى إذا تم تفريغ ViewController من الذاكرة بسبب ضغط الموارد ثم تمت استعادته. في هذه الحالة، سيكون viewDidDisappear سابقًا لـ viewDidLoad المتكرر.
تشير المعلمة animated في توقيع الطريقة إلى ما إذا كان الانتقال متحركًا أم لا. هذا مفيد للتمييز بين الانتقالات البرمجية بدون رسم متحرك (على سبيل المثال، عند تعيين rootViewController) والانتقالات المتحركة التي بدأها المستخدم. إذا كانت القيمة false، فربما تم إخفاء وحدة التحكم بواسطة النظام قسرًا — في هذه الحالة، قد تكون بعض العمليات المعتمدة على الوقت غير ذات صلة.
يستدعي النظام viewDidDisappear في سيناريوهين بالضبط: عند إزالة ViewController من مكدس التنقل وعندما يتم تغطيته بوحدة تحكم أخرى. في كلتا الحالتين، تشير الطريقة إلى أن العرض لم يعد مرئيًا للمستخدم، ويجب على المطور تحرير الموارد غير الضرورية في الخلفية. فهم هذه السيناريوهات يمنع الافتراضات الخاطئة حول حالة التطبيق.
السيناريو الأول — pop من UINavigationController. عندما يضغط المستخدم على زر الرجوع، يتم استدعاء popViewController:animated. تتلقى وحدة التحكم الحالية viewDidDisappear، ثم إذا لم يكن هناك المزيد من المراجع القوية لها، deinit. السيناريو الثاني — present/dismiss. عند تقديم وحدة تحكم جديدة بشكل مشروط، تتلقى presentingViewController viewDidDisappear. عند الرفض، يتم استدعاء هذه الطريقة على وحدة التحكم التي تم تقديمها بشكل مشروط.
السيناريو الثالث، الأقل وضوحًا — إضافة child ViewController. إذا تمت إضافة وحدة تحكم فرعية جديدة إلى وحدة تحكم حاوية (على سبيل المثال، UIPageViewController أو UITabBarController)، تتلقى وحدة التحكم الفرعية النشطة viewDidDisappear. هذا أمر بالغ الأهمية للتطبيقات ذات علامات التبويب أو دوارات الصفحات — يجب أن يعلق كل تبديل علامة تبويب عمل الشاشة غير النشطة بشكل صحيح.
هناك استثناء مهم: إذا تم عرض UIViewController في نافذة مشروطة وقام المستخدم بإغلاقها بشكل تفاعلي عن طريق التمرير لأسفل، فقد لا يستدعي النظام viewDidDisappear إذا لم يكتمل التمرير. ظهر هذا السلوك في iOS 13 مع الرفض التفاعلي. يجب على المطورين معالجة الحالة من خلال UIAdaptivePresentationControllerDelegate وطريقة didDismiss لضمان استلام الحدث.
ميزة أخرى — تحذيرات الذاكرة. عندما تكون الذاكرة منخفضة، قد يقوم النظام بتفريغ عرض وحدة تحكم غير مرئية على الشاشة. في هذه الحالة، عادةً ما يتم استدعاء viewDidDisappear قبل التفريغ، ولكن يجب على المطور تكرار عمليات التنظيف المهمة بشكل حاسم في didReceiveMemoryWarning كشبكة أمان. هذا النهج يمنع فقدان البيانات في السيناريوهات القصوى.
يُستخدم viewDidDisappear لثلاث فئات رئيسية من العمليات: إيقاف الأنشطة، تحرير الموارد، وحفظ الحالة. كل فئة لها أفضل ممارساتها الخاصة التي طورها مجتمع مطوري iOS. دعونا نلقي نظرة على السيناريوهات الأكثر شيوعًا مع أمثلة التنفيذ.
الخطأ النموذجي هو الاشتراك في الإشعارات في viewDidLoad وعدم إلغاء الاشتراك أبدًا. هذا يؤدي إلى استدعاء المعالج على كائن تم تحريره، مما يسبب تعطلًا. النهج الصحيح هو الاشتراك في viewWillAppear وإلغاء الاشتراك في viewDidDisappear، مما يضمن أن الاشتراك نشط فقط أثناء عرض وحدة التحكم على الشاشة.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(
self,
selector: #selector(handleKeyboardShow),
name: UIResponder.keyboardWillShowNotification,
object: nil
)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
يضمن هذا النمط أن معالج الإشعارات نشط فقط عندما تكون وحدة التحكم مرئية على الشاشة. عند الانتقال إلى شاشة أخرى، تتم إزالة جميع الاشتراكات تلقائيًا، ويتم استعادتها عند العودة. هذا يزيد من موثوقية التطبيق ويزيل فئة من الأخطاء المتعلقة بالإشعارات.
دعونا نفحص مثالين عمليين لاستخدام viewDidDisappear في مشاريع حقيقية. يوضح المثال الأول إيقاف مؤقت عند إخفاء الشاشة، والثاني يظهر إنهاء مراقبة لوحة المفاتيح بشكل صحيح. يتبع كلا المثالين مبدأ تحرير الموارد عندما تكون وحدة التحكم غير نشطة.
إذا كان Timer يعمل على الشاشة لتحديث الواجهة (على سبيل المثال، العد التنازلي أو الدوارة)، فيجب إيقافه عند إخفاء وحدة التحكم. استمرار المؤقت في الخلفية لا يستهلك موارد المعالج فحسب، بل قد يتسبب أيضًا في استثناء عند محاولة تحديث واجهة غير مرئية.
class CountdownViewController: UIViewController {
private var countdownTimer: Timer?
private var remainingSeconds: Int = 60
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startTimer()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
invalidateTimer()
}
private func invalidateTimer() {
countdownTimer()?.invalidate()
countdownTimer = nil
}
}
في العديد من التطبيقات، يقوم AVPlayer بتشغيل الفيديو في مشغل مدمج. إذا انتقل المستخدم إلى شاشة أخرى، يجب إيقاف الفيديو تلقائيًا. تنفيذ هذا في viewDidDisappear يضمن أن الإيقاف المؤقت يحدث بعد إخفاء الشاشة بالكامل — وهذا يمنع وميض الإطار الأسود أثناء الانتقال.
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if player().timeControlStatus == .playing {
player().pause()
playerLayer().removeFromSuperlayer()
}
player = nil
}
إلغاء متغير player بعد الإيقاف المؤقت يحرر أيضًا الذاكرة التي تشغلها مخازن الفيديو المؤقتة. هذا النهج مهم بشكل خاص للتطبيقات ذات مقاطع الفيديو الطويلة، حيث يمكن أن تشغل المخازن المؤقتة عشرات الميغابايت. الجمع بين الإيقاف المؤقت وإلغاء المراجع يقلل من بصمة التطبيق في الخلفية.
غالبًا ما يتم الخلط بين viewDidDisappear و viewWillDisappear و deinit، ولكن لكل من هذه الطرق مجال مسؤوليتها الخاص. فهم الحدود بينها هو مفتاح الهندسة المستقرة لتطبيق iOS. الاستخدام غير الصحيح يمكن أن يؤدي إلى تحرير مزدوج للموارد أو، على العكس، إلى تسرب الموارد.
الفرق الرئيسي بين viewDidDisappear و viewWillDisappear هو لحظة الاستدعاء. يتم استدعاء viewWillDisappear عندما يكون العرض لا يزال مرئيًا ولكنه يستعد للاختفاء. هذا مناسب لحفظ البيانات المرئية (النص في حقول الإدخال). يتم استدعاء viewDidDisappear بعد اكتمال الرسم المتحرك، عندما يكون العرض غير مرئي بالتأكيد — مثالي لتحرير الموارد غير المرتبطة بالحالة المرئية.
deinit، على عكس viewDidDisappear، يتم استدعاؤه فقط عند تدمير كائن UIViewController في الذاكرة. إذا كانت وحدة التحكم مخفية ببساطة (على سبيل المثال، مغطاة بنافذة مشروطة)، فلن يتم استدعاء deinit. في هذه الحالة، viewDidDisappear هو النقطة الوحيدة لتنفيذ عمليات الإنهاء. يجب أن يتم التنظيف الكامل للموارد في deinit، لكن viewDidDisappear يتعامل مع التحرير المؤقت حتى الظهور التالي.
عند التطوير باستخدام SwiftUI، لا يتم استخدام طريقة viewDidDisappear — يتم استبدالها بالمعدل .onDisappear الذي يعمل بطريقة مماثلة. ومع ذلك، يفتقر SwiftUI إلى التحكم المباشر في دورة الحياة، ويعتمد المطورون على Combine وكائنات State لإدارة الموارد. بالنسبة لتطبيقات UIKit، يظل viewDidDisappear الأداة الأساسية لإدارة اختفاء الشاشة.
حتى مطوري iOS ذوي الخبرة يرتكبون أخطاء عند العمل مع viewDidDisappear. دعونا نفحص المشاكل الخمس الأكثر شيوعًا وطرق منعها. معرفة هذه الأنماط المضادة ستساعد في تجنب الأخطاء التي يصعب اكتشافها والمتعلقة بدورة حياة وحدة التحكم.
يجب إيلاء اهتمام خاص لـ سلامة الخيوط. إذا تم استدعاء viewDidDisappear على الخيط الرئيسي (وهو ما يضمنه UIKit)، ولكن تنظيف الموارد يتضمن عمليات غير متزامنة، يجب مزامنة الوصول إلى البيانات المشتركة. استخدام DispatchQueue.main.async داخل viewDidDisappear لتحديث الواجهة بعد إكمال مهمة غير متزامنة هو نهج شائع لكنه صحيح.
نمط مضاد مهم آخر — استدعاء طرق المفوض داخل viewDidDisappear التي قد تبدأ انتقالًا جديدًا أو عرضًا مشروطًا. هذا يخلق دورة حيث قد يتم استدعاء viewDidDisappear مرة أخرى قبل اكتمال الاستدعاء الأول. توصي Apple بتجنب العروض المشروطة داخل طرق دورة الحياة، ونقلها إلى معالجات أحداث منفصلة.
الأسئلة الشائعة
يتم استدعاء viewWillDisappear قبل بدء رسم الإخفاء، عندما يكون العرض لا يزال مرئيًا. يتم استدعاء viewDidDisappear بعد اختفاء العرض بالكامل. استخدم viewWillDisappear لحفظ البيانات و viewDidDisappear لتحرير الموارد.
نعم، استدعاء super.viewDidDisappear(animated) إلزامي. يستخدم UIKit هذه الطريقة للإشعارات الداخلية وإكمال حالة الانتقال. بدون استدعاء super، قد يحدث خلل في UINavigationController و UITabBarController.
نعم، مع الرفض التفاعلي في iOS 13+ (التمرير لأسفل)، قد لا يتم استدعاء الطريقة إذا لم تكتمل الإيماءة. لضمان استلام الحدث، استخدم المفوض UIAdaptivePresentationControllerDelegate وطريقة presentationControllerDidDismiss.
يتم استدعاء deinit فقط عند تدمير الكائن، بينما يتم استدعاء viewDidDisappear عند كل إخفاء. لتحرير الموارد في كل انتقال (على سبيل المثال، إلغاء الاشتراك في الإشعارات)، استخدم viewDidDisappear. للتنظيف النهائي عند إزالة وحدة التحكم، استخدم deinit.
في SwiftUI، بدلاً من viewDidDisappear، يُستخدم المعدل .onDisappear { }. يتم استدعاؤه عند اختفاء العرض من التسلسل الهرمي. على عكس UIKit، لا يضمن SwiftUI استدعاء onDisappear في جميع سيناريوهات الرسوم المتحركة.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا