Το viewWillDisappear είναι μια μέθοδος του UIViewController που καλεί το UIKit αμέσως πριν η οθόνη αρχίσει να εξαφανίζεται από την οθόνη του χρήστη. Σύμφωνα με το Apple Developer Documentation, αυτή η μέθοδος λαμβάνει την παράμετρο animated και ενεργοποιείται κατά την push, pop, present, dismiss και εναλλαγή καρτελών. viewWillDisappear είναι το κύριο σημείο για αποθήκευση κατάστασης και σωστό καθαρισμό πόρων.
Κύρια σημεία
viewWillDisappear είναι μια μέθοδος του UIViewController που καλεί το UIKit αμέσως πριν το View του ελεγκτή αρχίσει να εξαφανίζεται από την οθόνη. Αυτή τη στιγμή, η οθόνη είναι ακόμα ορατή στον χρήστη, αλλά η μετάβαση έχει ήδη ξεκινήσει: ο NavigationController έχει ξεκινήσει την κινούμενη εικόνα push/pop, το modal παράθυρο έχει αρχίσει να κλείνει ή το TabBar έχει αρχίσει να αλλάζει σε άλλη καρτέλα. Ο προγραμματιστής παρακάμπτει αυτή τη μέθοδο για να εκτελέσει λειτουργίες που απαιτούν η οθόνη να είναι ακόμα διαθέσιμη, αλλά ήδη προετοιμάζεται για απόκρυψη.
Σε αντίθεση με το viewDidDisappear, το οποίο ενεργοποιείται αφού η οθόνη κρυφτεί, το viewWillDisappear παρέχει την τελευταία ευκαιρία να αποθηκεύσετε δεδομένα και να ελευθερώσετε πόρους ενώ ο χρήστης βλέπει ακόμα τη διεπαφή. Αυτό είναι κρίσιμο για την εμπειρία χρήστη — η αποθήκευση ενός προσχεδίου ή η διακοπή ενός χρονοδιακόπτη πρέπει να συμβεί πριν ο χρήστης μεταβεί σε άλλη οθόνη.
Η μέθοδος λαμβάνει την παράμετρο animated, η οποία υποδεικνύει εάν η εξαφάνιση γίνεται με κινούμενη εικόνα. Η τιμή true σημαίνει ότι το UIKit εκτελεί τη μετάβαση με κινούμενη εικόνα, false — η οθόνη εξαφανίζεται ακαριαία, για παράδειγμα κατά dismiss χωρίς κινούμενη εικόνα ή κατά προγραμματιστική αφαίρεση από την ιεραρχία.
viewWillDisappear καλείται σε όλα τα σενάρια όταν η τρέχουσα οθόνη παύει να είναι ενεργή. Ας εξετάσουμε τις κύριες περιπτώσεις που είναι ειδικές για την ανάπτυξη iOS.
Όταν το UINavigationController εκτελεί push ενός νέου ελεγκτή, στον τρέχοντα ελεγκτή καλείται το viewWillDisappear στην αρχή της κινούμενης εικόνας μετάβασης. Αυτή τη στιγμή, η τρέχουσα οθόνη είναι ακόμα ορατή κάτω από τον νέο ελεγκτή που γλιστρά πάνω της. Αυτό είναι το τυπικό σενάριο όπου το viewWillDisappear ενεργοποιείται με animated = true.
Όταν ο χρήστης πατά το κουμπί πίσω ή εκτελεί μια διαδραστική χειρονομία σάρωσης προς τα πίσω, στον τρέχοντα ελεγκτή καλείται το viewWillDisappear. Κατά τη διαδραστική χειρονομία, αυτή η κλήση μπορεί να ακυρωθεί εάν ο χρήστης αλλάξει γνώμη και επαναφέρει την οθόνη στη θέση της. Αυτό είναι ένα σημαντικό χαρακτηριστικό που πρέπει να ληφθεί υπόψη κατά τον σχεδιασμό της αποθήκευσης κατάστασης.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
saveDraftData()
NotificationCenter.default.removeObserver(self)
}
Κατά το κλείσιμο ενός modal παραθύρου, το viewWillDisappear καλείται στον ελεγκτή που κλείνει στην αρχή της κινούμενης εικόνας dismiss. Αυτή τη στιγμή, τα αποτελέσματα μπορούν να σταλούν πίσω μέσω delegate ή closure, καθώς ο ελεγκτής που παρουσίασε το modal παράθυρο δεν έχει λάβει ακόμα τον έλεγχο.
Το UITabBarController καλεί viewWillDisappear στον ελεγκτή της εγκαταλειπόμενης καρτέλας αμέσως μετά το άγγιγμα άλλης καρτέλας από τον χρήστη. Εάν στην τρέχουσα καρτέλα υπάρχουν ενεργές διεργασίες — αναπαραγωγή πολυμέσων, φόρτωση αρχείου, χρονοδιακόπτης — εδώ τίθενται σε παύση ή διακόπτονται.
viewWillDisappear επιλύει συγκεκριμένες εργασίες διαχείρισης πόρων και κατάστασης. Ας εξετάσουμε τα βασικά σενάρια με παραδείγματα κώδικα.
Η σημαντικότερη εργασία του viewWillDisappear — αποθήκευση δεδομένων που ο χρήστης εισήγαγε ή άλλαξε στην τρέχουσα οθόνη. Πρόχειρα μηνυμάτων, επεξεργασμένα πεδία φόρμας, επιλεγμένες ρυθμίσεις — όλα αυτά πρέπει να αποθηκευτούν πριν η οθόνη εξαφανιστεί. Χρησιμοποιήστε Core Data, UserDefaults ή αποθήκευση αρχείων για μονιμότητα.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
guard hasUnsavedChanges else { return }
draftStorage.save(currentDraft)
}
NotificationCenter, KVO και Combine publishers στα οποία έχετε εγγραφεί στο viewWillAppear ή viewDidLoad πρέπει να ακυρωθούν στο viewWillDisappear. Εάν δεν το κάνετε, οι ειδοποιήσεις θα φτάνουν στην κρυμμένη οθόνη, προκαλώντας ενημερώσεις διεπαφής που ο χρήστης δεν βλέπει, ή χειρότερα — σφάλματα λόγω αναφορών σε ήδη ελευθερωμένα αντικείμενα.
Οι κινούμενες εικόνες UIView που ξεκίνησαν στο viewDidAppear και οι χρονοδιακόπτες που λειτουργούν μέσω Timer ή DispatchSource πρέπει να σταματήσουν στο viewWillDisappear. Οι συνεχιζόμενες κινούμενες εικόνες σε κρυμμένη οθόνη καταναλώνουν GPU και μπαταρία χωρίς κανένα όφελος για τον χρήστη. Σταματήστε τις ρητά καλώντας invalidate στους χρονοδιακόπτες και removeAllAnimations στα στρώματα.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
countdownTimer?.invalidate()
countdownTimer = nil
loadingIndicator.layer.removeAllAnimations()
}
Εάν ο ελεγκτής άνοιξε για να ληφθεί ένα αποτέλεσμα — επιλογή στοιχείου, εισαγωγή κειμένου, επιβεβαίωση ενέργειας — το viewWillDisappear είναι η τελευταία στιγμή που ο αρχικός ελεγκτής υπάρχει ακόμα στη στοίβα και μπορεί να λάβει δεδομένα. Καλέστε τον delegate ή το closure πριν κληθεί το deinit.
Η αξιόπιστη αποθήκευση της κατάστασης οθόνης είναι ένα από τα πιο δύσκολα καθήκοντα στην ανάπτυξη iOS. Το viewWillDisappear είναι ένα σημαντικό, αλλά όχι το μοναδικό στοιχείο της στρατηγικής. Ας εξετάσουμε μια ολοκληρωμένη προσέγγιση.
Επίπεδο 1 — αποθήκευση στο viewWillDisappear. Γρήγορη αποθήκευση ελαφρών δεδομένων που πρέπει να είναι διαθέσιμα αμέσως μετά την επιστροφή. Κατάλληλο για κατάσταση διεπαφής: θέση κύλισης, επιλεγμένο τμήμα, κείμενο σε πεδία εισαγωγής. Πρόβλημα: σε ακυρωμένη διαδραστική χειρονομία pop, η αποθήκευση γίνεται παρόλο που ο χρήστης παρέμεινε στην οθόνη — τα δεδομένα αντικαθίστανται χωρίς λόγο.
Επίπεδο 2 — αποθήκευση στο viewDidDisappear. Αντιγράφει την αποθήκευση από το πρώτο επίπεδο, αλλά ενεργοποιείται μόνο αφού η οθόνη είναι εγγυημένα κρυμμένη. Αυτή είναι η ασφάλεια έναντι ακυρωμένων χειρονομιών. Ωστόσο, εάν στο viewWillDisappear έχετε ήδη διαγραφεί από ειδοποιήσεις, το viewDidDisappear μπορεί να μην έχει πρόσβαση σε ορισμένα δεδομένα.
Επίπεδο 3 — αποθήκευση μέσω ειδοποιήσεων εφαρμογής. Τα UIApplication.willResignActiveNotification και UIApplication.didEnterBackgroundNotification υποκλέπτουν την ελαχιστοποίηση της εφαρμογής. Εάν ο χρήστης ελαχιστοποίησε την εφαρμογή, το viewWillDisappear μπορεί να μην κλήθηκε — αλλά η αποθήκευση μέσω αυτών των ειδοποιήσεων εγγυάται την ακεραιότητα των δεδομένων κατά τη λήξη της περιόδου σύνδεσης.
| Επίπεδο | Μέθοδος/Ειδοποίηση | Αξιοπιστία | Χρήση |
|---|---|---|---|
| 1 | viewWillDisappear | Υψηλή | Κατάσταση διεπαφής, πρόχειρα |
| 2 | viewDidDisappear | Πολύ υψηλή | Κρίσιμα δεδομένα |
| 3 | willResignActive | Μέγιστη | Κατά ελαχιστοποίηση |
Σύσταση: χρησιμοποιήστε συνδυασμό και των τριών επιπέδων για κρίσιμα δεδομένα χρήστη. Για μη κρίσιμη κατάσταση — το πρώτο επίπεδο είναι αρκετό. Είναι σημαντικό να μην αντικαθιστάτε τα ίδια δεδομένα πολλές φορές — χρησιμοποιήστε μια σημαία dirty που υποδεικνύει ότι τα δεδομένα έχουν αλλάξει από την τελευταία αποθήκευση.
Ιδιαίτερη προσοχή πρέπει να δοθεί στη στρατηγική για οθόνες CRUD, όπου ο χρήστης εισάγει δεδομένα. Σε τέτοιες οθόνες, δεν συνιστάται η αποθήκευση κάθε πάτηματος πλήκτρου στο viewWillDisappear — είναι υπερβολικό. Χρησιμοποιήστε αυτόματη αποθήκευση με καθυστέρηση (debounce) μέσω Timer και εφαρμόστε το viewWillDisappear μόνο για τελική εξαναγκασμένη αποθήκευση εάν υπάρχουν μη αποθηκευμένες αλλαγές. Μια τέτοια προσέγγιση εξισορροπεί μεταξύ απόδοσης και ασφάλειας δεδομένων.
Για εφαρμογές με Core Data, ένα επιπλέον μέτρο είναι η κλήση saveContext στο viewWillDisappear μόνο όταν υπάρχουν πραγματικές αλλαγές στο managed object context. Ο έλεγχος context.hasChanges πριν από την αποθήκευση αποτρέπει περιττές εγγραφές στο persistent store και παρατείνει τη διάρκεια ζωής της μπαταρίας της συσκευής. Συνδυάστε αυτόν τον έλεγχο με την καθολική αποθήκευση στο applicationDidEnterBackground.
Η λανθασμένη χρήση του viewWillDisappear μπορεί να οδηγήσει σε απώλεια δεδομένων, διαρροές μνήμης και ασταθή συμπεριφορά της εφαρμογής. Ας εξετάσουμε τα συνήθη λάθη των προγραμματιστών iOS.
Πρώτο λάθος — αποθήκευση δεδομένων μόνο στο viewWillDisappear. Όπως συζητήθηκε παραπάνω, στη διαδραστική χειρονομία pop η μέθοδος καλείται ακόμα κι αν η οθόνη δεν εξαφανίζεται. Εάν η αποθήκευση έχει παρενέργειες — αποστολή δεδομένων στον διακομιστή, αλλαγή κατάστασης — αυτό μπορεί να οδηγήσει σε ψευδείς ενεργοποιήσεις. Προσθέστε έλεγχο isBeingDismissed ή isMovingFromParent.
Δεύτερο λάθος — έλλειψη απεγγραφής από το NotificationCenter. Αυτή είναι μία από τις πιο συχνές διαρροές μνήμης στο iOS. Εάν εγγραφήκατε στο viewWillAppear στο UIResponder.keyboardWillShowNotification αλλά δεν απεγγραφήκατε στο viewWillDisappear, το closure συνεχίζει να καλείται. Κατά το deinit του ελεγκτή, το closure θα αναφέρεται σε ένα ελευθερωμένο αντικείμενο — σφάλμα της εφαρμογής είναι εγγυημένο.
Τρίτο λάθος — εκτέλεση βαριών σύγχρονων λειτουργιών. Η αποθήκευση μεγάλου όγκου δεδομένων, η εγγραφή στο Core Data ή στο σύστημα αρχείων στο viewWillDisappear μπλοκάρει το main thread. Εάν η λειτουργία διαρκεί περισσότερο από την κινούμενη εικόνα μετάβασης, το UIKit διακόπτει το νήμα και η διεπαφή παγώνει. Μεταφέρετε τις βαριές αποθηκεύσεις σε ουρές παρασκηνίου.
Τέταρτο λάθος — ξεχάσατε να καλέσετε το super. Η μη κλήση του super.viewWillDisappear μπορεί να διαταράξει τη λειτουργία του UINavigationController και UITabBarController, τα οποία χρησιμοποιούν αυτή τη μέθοδο για τις εσωτερικές τους καταστάσεις. Πάντα καλείτε το super πρώτο ή τελευταίο, σύμφωνα με την τεκμηρίωση της Apple.
Αυτό το πρόβλημα επιδεινώνεται στο iOS με ενεργή πολυδιεργασία και εναλλαγές μεταξύ εφαρμογών. Πέμπτο λάθος — χρήση του DispatchQueue.main.async μετά από αποθήκευση στο viewWillDisappear. Εάν στείλετε ασύγχρονα ένα μπλοκ στην κύρια ουρά μετά την κλήση του super.viewWillDisappear, δεν υπάρχει εγγύηση ότι ο ελεγκτής εξακολουθεί να υπάρχει τη στιγμή της εκτέλεσης του μπλοκ. Χρησιμοποιείτε πάντα αδύναμες αναφορές [weak self] μέσα στα closures για να αποτρέψετε την αναφορά σε ελευθερωμένη μνήμη και να αποτρέψετε το σφάλμα της εφαρμογής.
Συχνές ερωτήσεις
viewWillDisappear καλείται στην αρχή της εξαφάνισης, όταν η οθόνη είναι ακόμα ορατή. viewDidDisappear — αφού η οθόνη είναι πλήρως κρυμμένη και η κινούμενη εικόνα έχει ολοκληρωθεί.
Χρησιμοποιήστε το viewDidDisappear για επιβεβαίωση αποθήκευσης ή ελέγξτε τις ιδιότητες isMovingFromParent και isBeingDismissed μέσα στο viewWillDisappear για να προσδιορίσετε εάν η οθόνη θα εξαφανιστεί πραγματικά.
Ναι, υποχρεωτικά, εάν χρησιμοποιείτε μπλοκ ή επιλογείς με self. Το ARC δεν διαχειρίζεται συνδρομές στο NotificationCenter. Στο iOS 9+ για μπλοκ χρησιμοποιήστε αδύναμη αναφορά και απεγγραφείτε στο viewWillDisappear.
Με κανέναν τρόπο — το force quit δεν καλεί μεθόδους Lifecycle. Για εγγυημένη αποθήκευση κατά τον τερματισμό της εφαρμογής χρησιμοποιήστε το UIApplication.willTerminateNotification ή αποθηκεύστε δεδομένα σε πραγματικό χρόνο καθώς αλλάζουν.
Ναι, στη διαδραστική χειρονομία pop, το UIKit καλεί το viewWillDisappear αμέσως μετά την έναρξη της χειρονομίας. Εάν ο χρήστης ακυρώσει τη χειρονομία, η οθόνη παραμένει ορατή, αλλά η μέθοδος έχει ήδη ενεργοποιηθεί. Πάντα ελέγχετε το isMovingFromParent.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης