.onDisappear es un modificador de SwiftUI que ejecuta un closure cuando una View se elimina de la jerarquía de la interfaz. La llamada ocurre al cerrar una pantalla, cambiar de pestaña, descartar una ventana modal o desplazar un elemento fuera de la vista. Según la Documentación para desarrolladores de Apple (2026), onDisappear no garantiza la llamada en escenarios de fallo o cuando la aplicación es terminada por el watchdog del sistema. Más información sobre SwiftUI en el artículo sobre SwiftUI.
Puntos clave
.onDisappear es un modificador de View en SwiftUI que toma un closure Void y lo ejecuta cuando la View se elimina de la jerarquía. Junto con .onAppear, forma el ciclo de vida completo de una pantalla: aparición — trabajo — desaparición. Apple presentó onDisappear junto con SwiftUI en iOS 13 como análogo de viewDidDisappear de UIKit.
Sintácticamente, .onDisappear es idéntico a onAppear: modifica cualquier View y adjunta un closure que el renderizador de SwiftUI llama cuando la vista se elimina. A diferencia de UIKit, donde viewDidDisappear se activa solo después de completar la animación de transición, onDisappear en SwiftUI puede llamarse antes de que finalice la animación — en el momento en que la View se marca para su eliminación.
Sintaxis básica de onDisappear es tan concisa como la de onAppear. El modificador no acepta parámetros adicionales — solo el closure, que se ejecuta sincrónicamente en el hilo principal.
struct DetailView: View {
var body: some View {
Text("Detail Screen")
.onDisappear {
print("DetailView desapareció de la pantalla")
}
}
}
Mecanismo de llamada de onDisappear es opuesto al de onAppear: los elementos hijos reciben onDisappear primero, luego el padre. Esta regla child-first garantiza que los recursos hijos se liberen antes de que se liberen los recursos del padre. Si un elemento hijo depende de datos del padre, debe poder terminar correctamente sin el contexto del padre.
SwiftUI llama a onDisappear cuando la View se elimina del grafo de renderizado. Los desencadenantes incluyen: pop de NavigationStack, cambio de TabView, descarte de modal (sheet/fullScreenCover) o una View renderizada condicionalmente (if/switch). En List y ScrollView, onDisappear se llama cuando una celda se desplaza más allá del búfer de precarga.
Orden child-first significa que si un VStack tiene tres View hijas, onDisappear se llama primero para cada hijo en orden inverso, luego para el padre. Esto es crítico para una limpieza adecuada: los temporizadores hijos se cancelan antes de que el ViewModel padre libere los recursos compartidos.
struct ParentView: View {
var body: some View {
VStack {
ChildView(id: "A")
ChildView(id: "B")
}
.onDisappear {
print("Parent onDisappear — último")
}
}
}
struct ChildView: View {
let id: String
var body: some View {
Text("Child \(id)")
.onDisappear {
print("Child \(id) onDisappear")
}
}
}
Salida en consola: Child B onDisappear, Child A onDisappear, Parent onDisappear — último. El orden inverso en comparación con onAppear garantiza una cadena de limpieza correcta.
Escenarios de llamada de onDisappear dependen del tipo de contenedor. En NavigationStack, onDisappear se activa al hacer pop-to-root, pop-back normal o al descartar la pantalla con deslizamiento (interactivePopGestureRecognizer). En TabView, cambiar de pestaña llama a onDisappear para la pestaña oculta y onAppear para la mostrada — ambos modificadores se activan casi simultáneamente.
En Sheet y fullScreenCover, onDisappear se llama al descartar programáticamente (mediante @Environment(\.dismiss)) o con un gesto de deslizamiento hacia abajo. Un detalle importante: si se abrió un sheet pero el usuario cambió de aplicación, onDisappear NO se llama hasta el cierre real.
| Escenario | onDisappear se llama | Nota |
|---|---|---|
| Pop en NavigationStack | Sí | Inmediatamente después de la animación |
| Cambio de pestaña TabView | Sí | Pestaña actual |
| Descarte de sheet | Sí | Antes de completar la animación |
| Desplazamiento en List | Sí | Celda salió de la zona de precarga |
| Minimizar aplicación | No | Sin garantía de llamada |
| Fallo/watchdog kill | No | No se llama |
Casos de uso principales de onDisappear son la limpieza de recursos, guardar estado y el seguimiento. A diferencia de onAppear, las tareas de onDisappear se ejecutan al salir y no necesitan comprobaciones de duplicación ya que la View desaparece una sola vez.
Guardar estado en onDisappear es especialmente útil para formularios donde los datos deben guardarse al salir de la pantalla. Los temporizadores y suscripciones de Combine se cancelan en onDisappear para evitar fugas al volver a la pantalla.
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")
}
}
Cancelación del temporizador en onDisappear evita la ejecución de código después de que la pantalla ya esté cerrada. Sin cancelación, el temporizador podría intentar actualizar @State que ya no pertenece a la View actual, generando una advertencia en tiempo de ejecución.
Tiempo de permanencia en una pantalla es un escenario clásico de seguimiento. Registrar el tiempo en onAppear, calcular la diferencia en onDisappear y enviar un evento analítico con la duración de la sesión.
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 diferencia clave entre onDisappear y onAppear es el orden de llamada y las garantías de ejecución. onAppear se llama cuando se añade una View a la jerarquía y puede volver a llamarse al recrearse. onDisappear se llama al eliminar y solo se garantiza que se active en un cierre normal, no en escenarios de fallo.
Según WWDC 2024, Apple recomienda tratar onDisappear como un punto de limpieza, no como un punto de confirmación de datos. Los datos críticos (pagos, registros) deben guardarse en tiempo real, no en el momento en que la View desaparece, ya que no se garantiza que onDisappear se llame al minimizar la aplicación.
| Característica | .onAppear | .onDisappear |
|---|---|---|
| Momento de llamada | View añadida a la jerarquía | View eliminada de la jerarquía |
| Orden | Parent-first | Child-first |
| Garantía | Alta | No en fallos |
| Tarea principal | Inicialización | Limpieza |
| Llamada repetida | Al recrear la View | Una vez por desaparición |
Recomendación: use onDisappear solo para limpieza no crítica y seguimiento. Para la persistencia de datos, use scenePhase o las notificaciones applicationWillTerminate en AppDelegate.
Escenario 1: reproducción tipo YouTube. En la pantalla de detalles de video, onDisappear guarda la posición de reproducción en UserDefaults. Al reabrir, onAppear restaura la posición desde UserDefaults, creando una experiencia de visualización continua.
Escenario 2: cancelar suscripción de Combine. Si un ViewModel usa publicadores de Combine, onDisappear cancela la suscripción mediante cancellable?.cancel(). Esto evita actualizaciones de la UI después de salir de la pantalla, lo que es especialmente importante para listas con paginación y consultas de búsqueda.
Escenario 3: cerrar WebSocket. En aplicaciones con conexiones en tiempo real (mensajería, feeds de cotizaciones), onDisappear cierra la conexión WebSocket para ahorrar batería y datos. La reconexión ocurre en onAppear al volver a la pantalla.
WebSocket es un recurso típico que debe cerrarse al salir de la pantalla. En el siguiente ejemplo, onDisappear desconecta el socket y onAppear lo reconecta, lo que proporciona un ahorro significativo de datos para aplicaciones con múltiples pantallas.
struct ChatView: View {
@StateObject private var socket = WebSocketManager()
var body: some View {
ChatListView(messages: socket.messages)
.onAppear {
socket.connect()
}
.onDisappear {
socket.disconnect()
}
}
}
Importante: al cambiar entre pestañas de TabView, onDisappear de la pestaña actual y onAppear de la siguiente se activan casi simultáneamente. Para WebSocket esto puede causar un ciclo de desconexión-reconexión que genera carga innecesaria. La solución es usar un retardo o verificar si el socket es necesario en la siguiente pantalla.
Preguntas frecuentes
Sí, .onDisappear no garantiza la llamada en caso de fallo de la aplicación (crash, watchdog kill), minimización de la aplicación sin cerrar la pantalla o escenarios del sistema donde la aplicación se termina en segundo plano. Para datos críticos, use scenePhase o applicationWillTerminate.
.onDisappear se activa cuando una View se elimina de la jerarquía — un callback local para una pantalla específica. .scenePhase (mediante @Environment(\.scenePhase)) se activa cuando cambia el estado de toda la aplicación: active, inactive, background. Use onAppear+onDisappear para el seguimiento del tiempo en pantalla y scenePhase para la persistencia del estado global.
La causa es el cambio rápido de pestañas o el push/pop repetido de la misma pantalla. SwiftUI puede crear una nueva instancia de View, eliminar la anterior, crear de nuevo — cada vez llamando a onDisappear y onAppear. Verifique si está usando .id(), .equatable() o recreando la View en el body del padre.
Sí, .onDisappear es totalmente compatible con macOS (10.15+) con el mismo comportamiento: se llama al cerrar la ventana, eliminar un panel de split view o descartar un modal. En macOS, onDisappear también se llama al ocultar la ventana (no solo al cerrarla), lo que es importante tener en cuenta para las aplicaciones de macOS.
Guarde una referencia a URLSessionTask en @State y llame a task.cancel() en onDisappear. Alternativamente, use el modificador .task, que cancela automáticamente la operación asíncrona cuando la View desaparece. .task es preferible para todas las operaciones asíncronas, incluida URLSession.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también