.onDisappear è un modificatore SwiftUI che esegue una closure quando una View viene rimossa dalla gerarchia dell'interfaccia. La chiamata avviene alla chiusura di uno schermo, al cambio di scheda, alla chiusura di una finestra modale o quando un elemento scorre fuori dalla visibilità. Secondo la Documentazione per sviluppatori Apple (2026), onDisappear non garantisce la chiamata in scenari di crash o quando l'app viene terminata dal watchdog di sistema. Per saperne di più su SwiftUI, consulta l'articolo su SwiftUI.
Punti chiave
.onDisappear è un modificatore di View in SwiftUI che accetta una closure Void e la esegue quando la View viene rimossa dalla gerarchia. Insieme a .onAppear, forma il ciclo di vita completo di uno schermo: apparizione — lavoro — scomparsa. Apple ha introdotto onDisappear insieme a SwiftUI in iOS 13 come analogo di viewDidDisappear di UIKit.
Sintatticamente, .onDisappear è identico a onAppear: modifica qualsiasi View e collega una closure che il renderer di SwiftUI chiama quando la vista viene rimossa. A differenza di UIKit, dove viewDidDisappear si attiva solo dopo il completamento dell'animazione di transizione, onDisappear in SwiftUI può essere chiamato prima della fine dell'animazione — nel momento in cui la View viene contrassegnata per la rimozione.
Sintassi di base di onDisappear è concisa quanto quella di onAppear. Il modificatore non accetta parametri aggiuntivi — solo la closure, che viene eseguita in modo sincrono sul thread principale.
struct DetailView: View {
var body: some View {
Text("Detail Screen")
.onDisappear {
print("DetailView è scomparsa dallo schermo")
}
}
}
Meccanismo di chiamata di onDisappear è l'opposto di onAppear: gli elementi figli ricevono onDisappear per primi, poi il genitore. Questa regola child-first garantisce che le risorse figlie vengano liberate prima che le risorse genitore vengano rilasciate. Se un elemento figlio dipende dai dati del genitore, deve poter terminare correttamente senza il contesto genitore.
SwiftUI chiama onDisappear quando la View viene rimossa dal grafo di rendering. I trigger includono: pop da NavigationStack, cambio di TabView, chiusura di modale (sheet/fullScreenCover) o una View renderizzata condizionalmente (if/switch). In List e ScrollView, onDisappear viene chiamato quando una cella scorre oltre il buffer di precaricamento.
Ordine child-first significa che se un VStack ha tre View figlie, onDisappear viene chiamato prima per ogni figlio in ordine inverso, poi per il genitore. Questo è fondamentale per una corretta pulizia: i timer figli vengono annullati prima che il ViewModel genitore rilasci le risorse condivise.
struct ParentView: View {
var body: some View {
VStack {
ChildView(id: "A")
ChildView(id: "B")
}
.onDisappear {
print("Parent onDisappear — ultimo")
}
}
}
struct ChildView: View {
let id: String
var body: some View {
Text("Child \(id)")
.onDisappear {
print("Child \(id) onDisappear")
}
}
}
Output console: Child B onDisappear, Child A onDisappear, Parent onDisappear — ultimo. L'ordine inverso rispetto a onAppear garantisce una catena di pulizia corretta.
Scenari di chiamata di onDisappear dipendono dal tipo di contenitore. In NavigationStack, onDisappear si attiva su pop-to-root, pop-back normale o chiusura dello schermo con swipe (interactivePopGestureRecognizer). In TabView, cambiare scheda chiama onDisappear per la scheda nascosta e onAppear per quella mostrata — entrambi i modificatori si attivano quasi simultaneamente.
In Sheet e fullScreenCover, onDisappear viene chiamato alla chiusura programmatica (tramite @Environment(\.dismiss)) o con un gesto di swipe verso il basso. Un dettaglio importante: se un sheet è stato aperto ma l'utente ha cambiato app, onDisappear NON viene chiamato fino alla chiusura effettiva.
| Scenario | onDisappear chiamato | Nota |
|---|---|---|
| Pop in NavigationStack | Sì | Subito dopo l'animazione |
| Cambio scheda TabView | Sì | Scheda corrente |
| Chiusura sheet | Sì | Prima del completamento dell'animazione |
| Scorrimento in List | Sì | Cella uscita dalla zona di precaricamento |
| Minimizzazione app | No | Nessuna garanzia di chiamata |
| Crash/watchdog kill | No | Non chiamato |
Casi d'uso principali di onDisappear sono la pulizia delle risorse, il salvataggio dello stato e il tracciamento. A differenza di onAppear, le attività di onDisappear vengono eseguite all'uscita e non necessitano di controllo di duplicazione poiché la View scompare una sola volta.
Salvataggio dello stato in onDisappear è particolarmente utile per i moduli in cui i dati devono essere salvati quando si lascia lo schermo. I timer e le sottoscrizioni Combine vengono annullati in onDisappear per evitare perdite quando si torna allo schermo.
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")
}
}
Annullamento del timer in onDisappear impedisce l'esecuzione del codice dopo che lo schermo è già stato chiuso. Senza annullamento, il timer potrebbe tentare di aggiornare @State che non appartiene più alla View corrente, causando un avviso di runtime.
Tempo di permanenza sullo schermo è uno scenario classico di tracciamento. Registra il tempo in onAppear, calcola la differenza in onDisappear e invia un evento analitico con la durata della sessione.
struct TrackedView: View {
@State private var appearTime: Date?
var body: some View {
Text("Tracked Screen")
.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)
])
}
}
}
}
La differenza chiave tra onDisappear e onAppear è l'ordine di chiamata e le garanzie di esecuzione. onAppear viene chiamato quando una View viene aggiunta alla gerarchia e può essere richiamato alla ricreazione. onDisappear viene chiamato alla rimozione ed è garantito solo alla chiusura normale, non in scenari di crash.
Secondo la WWDC 2024, Apple raccomanda di considerare onDisappear come un punto di pulizia, non come un punto di commit dei dati. I dati critici (pagamenti, registrazioni) dovrebbero essere salvati in tempo reale, non al momento della scomparsa della View, poiché onDisappear non è garantito quando l'app viene minimizzata.
| Caratteristica | .onAppear | .onDisappear |
|---|---|---|
| Momento di chiamata | View aggiunta alla gerarchia | View rimossa dalla gerarchia |
| Ordine | Parent-first | Child-first |
| Garanzia | Alta | Non in caso di crash |
| Compito principale | Inizializzazione | Pulizia |
| Chiamata ripetuta | Alla ricreazione della View | Una volta per scomparsa |
Raccomandazione: usa onDisappear solo per pulizia non critica e tracciamento. Per la persistenza dei dati, usa scenePhase o le notifiche applicationWillTerminate in AppDelegate.
Scenario 1: riproduzione stile YouTube. Sullo schermo dei dettagli del video, onDisappear salva la posizione di riproduzione in UserDefaults. Alla riapertura, onAppear ripristina la posizione da UserDefaults, creando un'esperienza di visione continua.
Scenario 2: annullare sottoscrizione Combine. Se un ViewModel utilizza publisher Combine, onDisappear annulla la sottoscrizione tramite cancellable?.cancel(). Ciò impedisce aggiornamenti dell'UI dopo aver lasciato lo schermo, particolarmente importante per liste con paginazione e query di ricerca.
Scenario 3: chiudere WebSocket. Nelle app con connessioni in tempo reale (messaggeri, feed di quotazioni), onDisappear chiude la connessione WebSocket per risparmiare batteria e dati. La riconnessione avviene in onAppear al ritorno sullo schermo.
WebSocket è una risorsa tipica che deve essere chiusa quando si lascia lo schermo. Nell'esempio seguente, onDisappear disconnette il socket e onAppear lo riconnette, offrendo un notevole risparmio di dati per app con più schermate.
struct ChatView: View {
@StateObject private var socket = WebSocketManager()
var body: some View {
ChatListView(messages: socket.messages)
.onAppear {
socket.connect()
}
.onDisappear {
socket.disconnect()
}
}
}
Importante: quando si cambiano le schede di TabView, onDisappear della scheda corrente e onAppear della scheda successiva si attivano quasi contemporaneamente. Per WebSocket, ciò può causare un ciclo di disconnessione-riconnessione che crea carico inutile. La soluzione è utilizzare un ritardo o verificare se il socket è necessario sullo schermo successivo.
Domande frequenti
Sì, .onDisappear non garantisce la chiamata in caso di crash dell'app (crash, watchdog kill), minimizzazione dell'app senza chiudere lo schermo o scenari di sistema in cui l'app viene terminata in background. Per i dati critici, usa scenePhase o applicationWillTerminate.
.onDisappear si attiva quando una View viene rimossa dalla gerarchia — un callback locale per uno schermo specifico. .scenePhase (tramite @Environment(\.scenePhase)) si attiva quando lo stato dell'intera app cambia: active, inactive, background. Usa onAppear+onDisappear per il tracciamento del tempo sullo schermo e scenePhase per la persistenza dello stato globale.
La causa è il rapido cambio di schede o il push/pop ripetuto dello stesso schermo. SwiftUI può creare una nuova istanza di View, rimuovere quella vecchia, crearne un'altra — ogni volta chiamando onDisappear e onAppear. Verifica se stai usando .id(), .equatable() o ricreando la View nel body genitore.
Sì, .onDisappear è completamente supportato su macOS (10.15+) con lo stesso comportamento: viene chiamato alla chiusura della finestra, alla rimozione di un pannello di split view o alla chiusura di un modale. Su macOS, onDisappear viene chiamato anche quando si nasconde la finestra (non solo quando si chiude), il che è importante da considerare per le app macOS.
Salva un riferimento a URLSessionTask in @State e chiama task.cancel() in onDisappear. In alternativa, usa il modificatore .task che annulla automaticamente l'operazione asincrona quando la View scompare. .task è preferito per tutte le operazioni asincrone, incluso URLSession.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche