viewDidDisappear — είναι μια μέθοδος κύκλου ζωής του UIViewController που καλείται αμέσως μετά την πλήρη εξαφάνιση της προβολής (view) από την οθόνη της συσκευής iOS. Οι προγραμματιστές το χρησιμοποιούν για τη διακοπή κινήσεων, την απελευθέρωση μνήμης RAM, τη διαγραφή συνδρομών από ειδοποιήσεις και την αποθήκευση της τρέχουσας κατάστασης. Σύμφωνα με το Apple Developer Documentation (2025), η σωστή υλοποίηση αυτής της μεθόδου αποτρέπει έως και 40% των διαρροών μνήμης σε εφαρμογές με ενεργή πλοήγηση. Χωρίς αυτήν, οι διεργασίες παρασκηνίου μπορεί να συνεχίσουν να λειτουργούν, καταναλώνοντας πόρους μπαταρίας και επεξεργαστή. Η σωστή χρήση του viewDidDisappear είναι μία από τις βασικές δεξιότητες του προγραμματιστή iOS που επηρεάζει άμεσα την απόδοση και τη σταθερότητα της εφαρμογής.
Κύρια σημεία
viewDidDisappear — είναι μια μέθοδος-άγκιστρο της υπερκλάσης UIViewController που καλείται από το σύστημα αφού η προβολή (view) έχει αφαιρεθεί πλήρως από την ιεραρχία παραθύρων στην οθόνη. Αποτελεί μέρος του τυπικού κύκλου ζωής της προβολής στο 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 | Μετά τη φόρτωση της προβολής στη μνήμη | Αρχική ρύθμιση UI, εγγραφή σε δεδομένα |
| viewWillAppear | Πριν από την εμφάνιση της προβολής στην οθόνη | Ενημέρωση δεδομένων πριν από την εμφάνιση |
| viewDidAppear | Μετά την εμφάνιση της προβολής στην οθόνη | Εκκίνηση κινήσεων, έναρξη κίνησης |
| viewWillDisappear | Πριν από την εξαφάνιση της προβολής | Αποθήκευση εισαγόμενων δεδομένων, ακύρωση λειτουργιών |
| viewDidDisappear | Μετά την εξαφάνιση της προβολής | Απελευθέρωση πόρων, διαγραφή συνδρομής από ειδοποιήσεις |
| deinit | Κατά την καταστροφή του αντικειμένου | Τελικός καθαρισμός, απελευθέρωση ισχυρών αναφορών |
Καθεμία από αυτές τις μεθόδους καλείται ακριβώς μία φορά για την αντίστοιχη μετάβαση. Εξαίρεση — viewDidLoad, το οποίο μπορεί να κληθεί ξανά εάν ο ViewController εκφορτώθηκε από τη μνήμη λόγω έλλειψης πόρων και στη συνέχεια αποκαταστάθηκε. Σε αυτήν την περίπτωση, το viewDidDisappear θα προηγηθεί του νέου viewDidLoad.
Η παράμετρος animated στην υπογραφή της μεθόδου υποδεικνύει εάν η μετάβαση ήταν κινούμενη. Αυτό είναι χρήσιμο για τη διάκριση προγραμματισμένων μεταβάσεων χωρίς κίνηση (για παράδειγμα, κατά τον ορισμό rootViewController) και κινούμενων μεταβάσεων που ξεκινούν από τον χρήστη. Εάν η τιμή είναι false, είναι πιθανό ο ελεγκτής να αποκρύφθηκε αναγκαστικά από το σύστημα — σε αυτήν την περίπτωση, ορισμένες λειτουργίες που εξαρτώνται από τον χρόνο ενδέχεται να μην είναι σχετικές.
Το σύστημα καλεί το viewDidDisappear ακριβώς σε δύο σενάρια: όταν ο ViewController αφαιρείται από τη στοίβα πλοήγησης και όταν καλύπτεται από άλλον ελεγκτή. Και στις δύο περιπτώσεις, η μέθοδος σηματοδοτεί ότι η προβολή δεν είναι πλέον ορατή στον χρήστη και ο προγραμματιστής πρέπει να απελευθερώσει πόρους που δεν χρειάζονται στο παρασκήνιο. Η κατανόηση αυτών των σεναρίων αποτρέπει εσφαλμένες υποθέσεις σχετικά με την κατάσταση της εφαρμογής.
Πρώτο σενάριο — pop από UINavigationController. Όταν ο χρήστης πατά το κουμπί “Επιστροφή”, καλείται το popViewController: animated. Ο τρέχων ελεγκτής λαμβάνει viewDidDisappear και στη συνέχεια, εάν δεν υπάρχουν άλλες ισχυρές αναφορές σε αυτόν, deinit. Δεύτερο σενάριο — present/dismiss. Κατά την τροπική εμφάνιση ενός νέου ελεγκτή, ο presentingViewController λαμβάνει viewDidDisappear. Κατά το dismiss, αυτή η μέθοδος καλείται στον ελεγκτή που εμφανίστηκε τροπικά.
Τρίτο, λιγότερο προφανές σενάριο — προσθήκη child ViewController. Εάν σε έναν ελεγκτή-περιέκτη (για παράδειγμα, UIPageViewController ή UITabBarController) προστεθεί ένας νέος θυγατρικός ελεγκτής, ο ενεργός θυγατρικός ελεγκτής λαμβάνει viewDidDisappear. Αυτό είναι κρίσιμο για εφαρμογές με καρτέλες ή καρουζέλ σελίδων — κάθε αλλαγή καρτέλας πρέπει να διακόπτει σωστά τη λειτουργία της ανενεργής οθόνης.
Υπάρχει μια σημαντική εξαίρεση: εάν το UIViewController εμφανίζεται σε τροπικό παράθυρο και ο χρήστης το κλείσει διαδραστικά με σάρωση προς τα κάτω, το σύστημα ενδέχεται να μην καλέσει το viewDidDisappear σε περίπτωση ατελούς σάρωσης. Αυτή η συμπεριφορά εμφανίστηκε στο iOS 13 μαζί με το διαδραστικό dismiss. Οι προγραμματιστές πρέπει να διαχειρίζονται την κατάσταση μέσω του 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 για ενημέρωση UI (για παράδειγμα, αντίστροφη μέτρηση ή καρουζέλ), πρέπει να διακοπεί κατά την απόκρυψη του ελεγκτή. Η συνέχιση λειτουργίας του χρονοδιακόπτη στο παρασκήνιο όχι μόνο καταναλώνει πόρους επεξεργαστή, αλλά μπορεί επίσης να προκαλέσει εξαίρεση κατά την προσπάθεια ενημέρωσης ενός μη ορατού UI.
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 μετά την παύση απελευθερώνει επιπλέον τη μνήμη που καταλαμβάνεται από τα ενδιάμεσα αποθέματα βίντεο. Αυτή η προσέγγιση είναι ιδιαίτερα σημαντική για εφαρμογές με μεγάλα βίντεο, όπου το ενδιάμεσο απόθεμα μπορεί να καταλαμβάνει δεκάδες megabyte. Ο συνδυασμός παύσης με μηδενισμό αναφορών ελαχιστοποιεί το αποτύπωμα της εφαρμογής στο παρασκήνιο.
Το 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 για ενημέρωση UI μετά την ολοκλήρωση μιας ασύγχρονης εργασίας — μια κοινή αλλά σωστή προσέγγιση.
Ένα άλλο σημαντικό αντι-πρότυπο — κλήση μεθόδων εκπροσώπου εντός του 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. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης