.onDisappear — ένας τροποποιητής SwiftUI που εκτελεί ένα closure όταν ένα View αφαιρείται από την ιεραρχία διεπαφής. Η κλήση γίνεται κατά το κλείσιμο της οθόνης, την αλλαγή καρτέλας, το dismiss modal παραθύρου ή το scroll ενός στοιχείου εκτός ορατής περιοχής. Σύμφωνα με το Apple Developer Documentation (2026), το onDisappear δεν εγγυάται κλήση σε σενάρια έκτακτης τερματισμού ή καταστροφής της εφαρμογής από system-watchdog. Διαβάστε περισσότερα για το SwiftUI στο υλικό για το SwiftUI.
Κύρια σημεία
.onDisappear — ένας τροποποιητής View στο SwiftUI που δέχεται ένα Void closure και το εκτελεί όταν το View αφαιρείται από την ιεραρχία. Μαζί με το .onAppear σχηματίζει τον πλήρη κύκλο ζωής της οθόνης: εμφάνιση — λειτουργία — εξαφάνιση. Η Apple εισήγαγε το onDisappear μαζί με την κυκλοφορία του SwiftUI στο iOS 13 ως ανάλογο του viewDidDisappear από το UIKit.
Συντακτικά .onDisappear είναι πανομοιότυπο με το onAppear: τροποποιεί οποιοδήποτε View και επισυνάπτει ένα closure που καλείται από το στοιχείο SwiftUI κατά την αφαίρεση της προβολής. Σε αντίθεση με το UIKit, όπου το viewDidDisappear ενεργοποιείται μόνο μετά την ολοκλήρωση του animation μετάβασης, το onDisappear στο SwiftUI μπορεί να κληθεί πριν από την ολοκλήρωση του animation — τη στιγμή που το View επισημαίνεται για αφαίρεση.
Βασική σύνταξη του onDisappear είναι εξίσου συνοπτική με του onAppear. Ο τροποποιητής δεν δέχεται πρόσθετες παραμέτρους — μόνο το closure που εκτελείται σύγχρονα στο κύριο νήμα.
struct DetailView: View {
var body: some View {
Text("Οθόνη Λεπτομερειών")
.onDisappear {
print("Το DetailView εξαφανίστηκε από την οθόνη")
}
}
}
Μηχανισμός κλήσης του onDisappear είναι αντίθετος του onAppear: πρώτα τα θυγατρικά στοιχεία λαμβάνουν onDisappear, μετά ο γονέας. Αυτός ο κανόνας child-first εγγυάται ότι οι πόροι των θυγατρικών στοιχείων απελευθερώνονται πριν από τους πόρους του γονέα. Εάν ένα θυγατρικό στοιχείο εξαρτάται από δεδομένα του γονέα, πρέπει να μπορεί να τερματιστεί σωστά χωρίς το περιβάλλον του γονέα.
SwiftUI καλεί το onDisappear τη στιγμή που το View εξαιρείται από το γράφημα απόδοσης. Ενεργοποιητής μπορεί να είναι: pop από NavigationStack, αλλαγή TabView, dismiss modal παραθύρου (sheet/fullScreenCover), αλλαγή υπό όρους εμφανιζόμενου View (if/switch). Σε List και ScrollView το onDisappear καλείται όταν ένα κελί γίνεται scroll εκτός του προσωρινού buffer prefetch.
Σειρά child-first σημαίνει ότι αν υπάρχουν τρία θυγατρικά View σε ένα VStack, το onDisappear θα κληθεί πρώτα για κάθε παιδί με αντίστροφη σειρά, μετά για τον γονέα. Αυτό είναι κρίσιμο για σωστό καθαρισμό: τα θυγατρικά χρονόμετρα ακυρώνονται προτού το ViewModel του γονέα απελευθερώσει κοινόχρηστους πόρους.
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("Παιδί \(id)")
.onDisappear {
print("Παιδί \(id) onDisappear")
}
}
}
Έξοδος κονσόλας: Child B onDisappear, Child A onDisappear, Parent onDisappear — τελευταίο. Η αντίστροφη σειρά σε σύγκριση με το onAppear εγγυάται μια σωστή αλυσίδα απελευθέρωσης.
Σενάρια κλήσης του onDisappear εξαρτώνται από τον τύπο του περιέκτη. Στο NavigationStack το onDisappear ενεργοποιείται σε pop-to-root, συνηθισμένο pop-back ή αφαίρεση οθόνης με χειρονομία σύρσιμου (interactivePopGestureRecognizer). Στο TabView η αλλαγή καρτέλας ενεργοποιεί onDisappear για την κρυφή και onAppear για την εμφανιζόμενη — και οι δύο τροποποιητές ενεργοποιούνται σχεδόν ταυτόχρονα.
Σε Sheet και fullScreenCover το onDisappear καλείται σε προγραμματισμένο dismiss (μέσω @Environment(\.dismiss)) ή χειρονομία σύρσιμου προς τα κάτω. Σημαντικό χαρακτηριστικό: αν το sheet ήταν ανοιχτό αλλά ο χρήστης άλλαξε εφαρμογή, το onDisappear ΔΕΝ καλείται μέχρι το πραγματικό κλείσιμο.
| Σενάριο | onDisappear καλείται | Σημείωση |
|---|---|---|
| Pop στο NavigationStack | Ναι | Αμέσως μετά το animation |
| Αλλαγή καρτέλας TabView | Ναι | Τρέχουσα καρτέλα |
| Dismiss sheet | Ναι | Πριν την ολοκλήρωση animation |
| Scroll σε List | Ναι | Κελί βγήκε από ζώνη prefetch |
| Ελαχιστοποίηση εφαρμογής | Όχι | Καμία εγγύηση κλήσης |
| Crash/watchdog kill | Όχι | Δεν καλείται |
Κύρια σενάρια του onDisappear — καθαρισμός πόρων, αποθήκευση κατάστασης και παρακολούθηση. Σε αντίθεση με το onAppear, οι εργασίες onDisappear εκτελούνται κατά την έξοδο και δεν απαιτούν έλεγχο διπλοτύπου, καθώς το View εξαφανίζεται μία φορά.
Αποθήκευση κατάστασης στο onDisappear είναι ιδιαίτερα χρήσιμη για φόρμες όπου τα δεδομένα πρέπει να αποθηκεύονται κατά την έξοδο από την οθόνη. Τα χρονόμετρα και οι συνδρομές Combine ακυρώνονται στο onDisappear για να αποφευχθούν διαρροές μνήμης κατά την επιστροφή στην οθόνη.
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 και στέλνουμε ένα αναλυτικό συμβάν με τη διάρκεια συνεδρίας.
struct TrackedView: View {
@State private var appearTime: Date?
var body: some View {
Text("Παρακολουθούμενη Οθόνη")
.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 — η σειρά κλήσης και οι εγγυήσεις ενεργοποίησης. Το onAppear καλείται όταν το View προστίθεται στην ιεραρχία και έχει την ιδιότητα επανακλήσης κατά την αναδημιουργία. Το onDisappear καλείται κατά την αφαίρεση και εγγυημένα λειτουργεί μόνο σε κανονικό κλείσιμο, αλλά όχι σε σενάρια έκτακτης ανάγκης.
Σύμφωνα με το WWDC 2024, η Apple συνιστά να θεωρείται το onDisappear ως σημείο καθαρισμού, όχι ως σημείο αποθήκευσης δεδομένων. Τα κρίσιμα σημαντικά δεδομένα (πληρωμές, εγγραφές) πρέπει να αποθηκεύονται σε πραγματικό χρόνο, όχι τη στιγμή εξαφάνισης του View, επειδή το onDisappear δεν εγγυάται κλήση κατά την ελαχιστοποίηση της εφαρμογής.
| Χαρακτηριστικό | .onAppear | .onDisappear |
|---|---|---|
| Στιγμή κλήσης | Προσθήκη View στην ιεραρχία | Αφαίρεση View από την ιεραρχία |
| Σειρά | Parent-first | Child-first |
| Εγγύηση | Υψηλή | Όχι σε έκτακτο τερματισμό |
| Κύρια εργασία | Αρχικοποίηση | Καθαρισμός |
| Επανακλήση | Σε αναδημιουργία View | Μία φορά ανά εξαφάνιση |
Σύσταση: χρησιμοποιείτε το onDisappear μόνο για μη κρίσιμο καθαρισμό και παρακολούθηση. Για αποθήκευση δεδομένων χρησιμοποιείτε scenePhase ή ειδοποιήσεις applicationWillTerminate στο AppDelegate.
Σενάριο 1: φόρτωση τύπου YouTube. Στην οθόνη λεπτομερειών βίντεο το onDisappear αποθηκεύει τη θέση αναπαραγωγής στο UserDefaults. Κατά το εκ νέου άνοιγμα, το onAppear επαναφέρει τη θέση από το UserDefaults, δίνοντας εφέ συνεχούς παρακολούθησης.
Σενάριο 2: ακύρωση συνδρομής Combine. Εάν το ViewModel χρησιμοποιεί εκδότες Combine, το onDisappear ακυρώνει τη συνδρομή μέσω cancellable?.cancel(). Αυτό αποτρέπει την ενημέρωση του UI μετά την έξοδο από την οθόνη, κάτι που είναι ιδιαίτερα σημαντικό για λίστες με σελιδοποίηση και ερωτήματα αναζήτησης.
Σενάριο 3: κλείσιμο WebSocket. Σε εφαρμογές με συνδέσεις πραγματικού χρόνου (messengers, ροές τιμών) το onDisappear κλείνει τη σύνδεση WebSocket για εξοικονόμηση μπαταρίας και κίνησης. Το εκ νέου άνοιγμα γίνεται στο onAppear κατά την επιστροφή στην οθόνη.
WebSocket — ένας τυπικός πόρος που πρέπει να κλείνει κατά την έξοδο από την οθόνη. Στο παρακάτω παράδειγμα το onDisappear αποσυνδέει το socket και το onAppear το επανασυνδέει, προσφέροντας σημαντική εξοικονόμηση κίνησης για εφαρμογές με πολλές οθόνες.
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 αυτό μπορεί να οδηγήσει σε έναν κύκλο disconnect-connect που δημιουργεί πρόσθετο φορτίο. Λύση — χρήση καθυστέρησης ή έλεγχος αν το socket χρειάζεται στην επόμενη οθόνη.
Συχνές Ερωτήσεις
Ναι, το .onDisappear δεν εγγυάται κλήση σε έκτακτο τερματισμό της εφαρμογής (crash, watchdog kill), ελαχιστοποίηση της εφαρμογής χωρίς κλείσιμο οθόνης ή σενάριο συστήματος όπου η εφαρμογή καταστρέφεται στο παρασκήνιο. Για κρίσιμα σημαντικά δεδομένα χρησιμοποιείτε scenePhase ή applicationWillTerminate.
.onDisappear ενεργοποιείται κατά την αφαίρεση του View από την ιεραρχία — ένα τοπικό callback για μια συγκεκριμένη οθόνη. .scenePhase (μέσω @Environment(\.scenePhase)) ενεργοποιείται όταν αλλάζει η κατάσταση ολόκληρης της εφαρμογής: active, inactive, background. Για παρακολούθηση χρόνου στην οθόνη χρησιμοποιήστε onAppear+onDisappear, για αποθήκευση καθολικής κατάστασης — scenePhase.
Η αιτία — γρήγορη εναλλαγή μεταξύ καρτελών ή επαναλαμβανόμενο push/pop της ίδιας οθόνης. Το SwiftUI μπορεί να δημιουργήσει ένα νέο στιγμιότυπο View, να αφαιρέσει το παλιό, να δημιουργήσει ξανά — κάθε φορά ενεργοποιώντας onDisappear και onAppear. Ελέγξτε αν χρησιμοποιείτε .id(), .equatable() ή αν αναδημιουργείτε το View στο σώμα του γονέα.
Ναι, το .onDisappear υποστηρίζεται πλήρως σε macOS (10.15+) με την ίδια συμπεριφορά: καλείται κατά το κλείσιμο παραθύρου, αφαίρεση πίνακα split view ή dismiss modal παραθύρου. Σε macOS το onDisappear καλείται επίσης κατά την απόκρυψη παραθύρου (hide), όχι μόνο κατά το κλείσιμο, κάτι που είναι σημαντικό για εφαρμογές macOS.
Αποθηκεύστε την αναφορά στο URLSessionTask σε @State και καλέστε task.cancel() στο onDisappear. Εναλλακτικά, χρησιμοποιήστε τον τροποποιητή .task που ακυρώνει αυτόματα την async λειτουργία κατά την εξαφάνιση του View. Το .task είναι προτιμότερο για όλες τις async λειτουργίες, συμπεριλαμβανομένου του URLSession.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης