.onDisappear — un modificator SwiftUI care execută o închidere la ștergerea unui View din ierarhia interfeței. Apelul are loc la închiderea ecranului, schimbarea filei, dismiss-ul unei ferestre modale sau derularea unui element în afara zonei vizibile. Conform Apple Developer Documentation (2026), onDisappear nu garantează apelul în scenarii de terminare accidentală sau la distrugerea aplicației de către system-watchdog. Citiți mai multe despre SwiftUI în materialul despre SwiftUI.
Principalele
.onDisappear — un modificator View în SwiftUI care acceptă o închidere Void și o execută la ștergerea View-ului din ierarhie. Împreună cu .onAppear formează ciclul complet de viață al ecranului: apariție — lucru — dispariție. Apple a introdus onDisappear odată cu lansarea SwiftUI în iOS 13 ca analog al viewDidDisappear din UIKit.
Sintactic .onDisappear este identic cu onAppear: modifică orice View și atașează o închidere care este apelată de componenta SwiftUI la ștergerea vizualizării. Spre deosebire de UIKit, unde viewDidDisappear se declanșează doar după finalizarea animației de tranziție, onDisappear în SwiftUI poate fi apelat înainte de finalizarea animației — în momentul în care View este marcat pentru ștergere.
Sintaxa de bază a onDisappear este la fel de concisă ca cea a onAppear. Modificatorul nu acceptă parametri suplimentari — doar închiderea care se execută sincron în thread-ul principal.
struct DetailView: View {
var body: some View {
Text("Ecranul de Detalii")
.onDisappear {
print("DetailView a dispărut de pe ecran")
}
}
}
Mecanismul de apel al onDisappear este opus onAppear: mai întâi elementele copil primesc onDisappear, apoi părintele. Această regulă child-first garantează că resursele elementelor copil sunt eliberate înaintea resurselor părintelui. Dacă un element copil depinde de datele părintelui, trebuie să se poată finaliza corect fără contextul părintelui.
SwiftUI apelează onDisappear în momentul în care View este exclus din graful de randare. Declanșatorul poate fi: pop din NavigationStack, schimbarea TabView, dismiss-ul unei ferestre modale (sheet/fullScreenCover), schimbarea unui View afișat condiționat (if/switch). În List și ScrollView onDisappear este apelat la derularea celulei în afara buffer-ului de prefetch.
Ordinea child-first înseamnă că dacă într-un VStack sunt trei View-uri copil, onDisappear va fi apelat mai întâi pentru fiecare copil în ordine inversă, apoi pentru părinte. Acest lucru este critic pentru curățarea corectă: timer-ele copil sunt anulate înainte ca ViewModel-ul părintelui să elibereze resursele partajate.
struct ParentView: View {
var body: some View {
VStack {
ChildView(id: "A")
ChildView(id: "B")
}
.onDisappear {
print("Parent onDisappear — ultimul")
}
}
}
struct ChildView: View {
let id: String
var body: some View {
Text("Copil \(id)")
.onDisappear {
print("Copil \(id) onDisappear")
}
}
}
Ieșirea în consolă: Child B onDisappear, Child A onDisappear, Parent onDisappear — ultimul. Ordinea inversă față de onAppear garantează un lanț corect de eliberare.
Scenariile de apel ale onDisappear depind de tipul containerului. În NavigationStack onDisappear se declanșează la pop-to-root, pop-back obișnuit sau eliminarea ecranului prin gest de glisare (interactivePopGestureRecognizer). În TabView schimbarea filei declanșează onDisappear pentru cea ascunsă și onAppear pentru cea afișată — ambele modificatoare se declanșează aproape simultan.
În Sheet și fullScreenCover onDisappear este apelat la dismiss programatic (prin @Environment(\.dismiss)) sau prin gest de glisare în jos. O caracteristică importantă: dacă sheet-ul a fost deschis, dar utilizatorul a comutat aplicația, onDisappear NU este apelat până la închiderea efectivă.
| Scenariu | onDisappear este apelat | Notă |
|---|---|---|
| Pop în NavigationStack | Da | Imediat după animație |
| Schimbarea filei TabView | Da | Fila curentă |
| Dismiss sheet | Da | Înainte de finalizarea animației |
| Derulare în List | Da | Celula a ieșit din zona de prefetch |
| Minimizarea aplicației | Nu | Nu există garanție de apel |
| Crash/watchdog kill | Nu | Nu este apelat |
Scenariile principale ale onDisappear — curățarea resurselor, salvarea stării și urmărirea. Spre deosebire de onAppear, sarcinile onDisappear se execută la ieșire și nu necesită verificarea duplicării, deoarece View dispare o singură dată.
Salvarea stării în onDisappear este utilă în special pentru formulare, unde datele trebuie salvate la părăsirea ecranului. Timer-ele și abonamentele Combine sunt anulate în onDisappear pentru a evita scurgerile de memorie la revenirea pe ecran.
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")
}
}
Anularea timer-ului în onDisappear previne executarea codului după ce ecranul a fost deja închis. Fără anulare, timer-ul poate încerca să actualizeze un @State care nu mai aparține View-ului curent, generând un runtime warning.
Durata de staționare pe ecran — un scenariu clasic de urmărire. Înregistrăm timpul în onAppear, calculăm diferența în onDisappear și trimitem un eveniment analitic cu durata sesiunii.
struct TrackedView: View {
@State private var appearTime: Date?
var body: some View {
Text("Ecran Urmărit")
.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)
])
}
}
}
}
Diferența cheie dintre onDisappear și onAppear — ordinea de apel și garanțiile de declanșare. onAppear este apelat la adăugarea View-ului în ierarhie și are proprietatea de re-apelare la recreare. onDisappear este apelat la ștergere și garantează funcționarea doar la închiderea normală, dar nu în scenarii accidentale.
Conform WWDC 2024, Apple recomandă să considerați onDisappear ca punct de curățare, nu ca punct de salvare a datelor. Datele critic de importante (plăți, înregistrări) trebuie salvate în timp real, nu în momentul dispariției View-ului, deoarece onDisappear nu garantează apelul la minimizarea aplicației.
| Caracteristică | .onAppear | .onDisappear |
|---|---|---|
| Momentul apelării | Adăugarea View în ierarhie | Ștergerea View din ierarhie |
| Ordinea | Parent-first | Child-first |
| Garanția | Ridicată | Nu la terminări accidentale |
| Sarcina principală | Inițializare | Curățare |
| Re-apelare | La recrearea View | O singură dată la dispariție |
Recomandare: utilizați onDisappear doar pentru curățare necritică și urmărire. Pentru salvarea datelor utilizați scenePhase sau notificările applicationWillTerminate în AppDelegate.
Scenariul 1: încărcare asemănătoare YouTube. Pe ecranul de detalii video onDisappear salvează poziția de redare în UserDefaults. La redeschidere, onAppear restabilește poziția din UserDefaults, oferind efect de vizionare continuă.
Scenariul 2: anularea abonamentului Combine. Dacă ViewModel utilizează publisher-i Combine, onDisappear anulează abonamentul prin cancellable?.cancel(). Aceasta previne actualizarea UI după părăsirea ecranului, ceea ce este important în special pentru liste cu paginare și interogări de căutare.
Scenariul 3: închiderea WebSocket. În aplicațiile cu conexiuni în timp real (mesagerie, fluxuri de cotații) onDisappear închide conexiunea WebSocket pentru economisirea bateriei și a traficului. Redeschiderea are loc în onAppear la revenirea pe ecran.
WebSocket — o resursă tipică care trebuie închisă la părăsirea ecranului. În exemplul de mai jos onDisappear deconectează socket-ul, iar onAppear îl reconectează, oferind o economie semnificativă de trafic pentru aplicațiile cu mai multe ecrane.
struct ChatView: View {
@StateObject private var socket = WebSocketManager()
var body: some View {
ChatListView(messages: socket.messages)
.onAppear {
socket.connect()
}
.onDisappear {
socket.disconnect()
}
}
}
Important: la comutarea între filele TabView, onDisappear al filei curente și onAppear al filei următoare se declanșează aproape simultan. Pentru WebSocket aceasta poate duce la un ciclu disconnect-connect care creează sarcină suplimentară. Soluția — utilizarea unei întârzieri sau verificarea dacă socket-ul este necesar pe ecranul următor.
Întrebări frecvente
Da, .onDisappear nu garantează apelul la terminarea accidentală a aplicației (crash, watchdog kill), minimizarea aplicației fără închiderea ecranului sau scenariul de sistem în care aplicația este distrusă în fundal. Pentru date critic importante utilizați scenePhase sau applicationWillTerminate.
.onDisappear se declanșează la ștergerea View-ului din ierarhie — un callback local pentru un ecran specific. .scenePhase (prin @Environment(\.scenePhase)) se declanșează la schimbarea stării întregii aplicații: active, inactive, background. Pentru urmărirea timpului pe ecran utilizați onAppear+onDisappear, pentru salvarea stării globale — scenePhase.
Cauza — comutarea rapidă între file sau push/pop repetat al aceluiași ecran. SwiftUI poate crea o nouă instanță View, șterge-o pe cea veche, crea din nou — de fiecare dată declanșând onDisappear și onAppear. Verificați dacă nu utilizați .id(), .equatable() sau dacă nu recreați View-ul în body-ul părintelui.
Da, .onDisappear este complet suportat pe macOS (10.15+) cu același comportament: apelat la închiderea ferestrei, ștergerea panoului split view sau dismiss-ul ferestrei modale. Pe macOS onDisappear este apelat și la ascunderea ferestrei (hide), nu doar la închidere, ceea ce este important pentru aplicațiile macOS.
Salvați referința la URLSessionTask în @State și apelați task.cancel() în onDisappear. Alternativ, utilizați modificatorul .task care anulează automat operația async la dispariția View-ului. .task este preferat pentru toate operațiile async, inclusiv URLSession.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și