.onDisappear: принцип на работа и приложение в SwiftUI

Автор: IT Sectr Публикувано: 2026-06-26 Време за четене: 10 мин

.onDisappear — модификатор SwiftUI, който изпълнява затваряне при премахване на View от йерархията на интерфейса. Извикването става при затваряне на екран, превключване на раздел, dismiss на модален прозорец или скролване на елемент извън видимата зона. Според Apple Developer Documentation (2026), onDisappear не гарантира извикване в сценарии на аварийно прекратяване или при унищожаване на приложението от system-watchdog. Прочетете повече за SwiftUI в материала за SwiftUI.

Основни точки

  • .onDisappear — модификатор SwiftUI за изпълнение на код при изчезване на View.
  • Симетричност — onDisappear е сдвоен с onAppear и се извиква в обратен ред.
  • Почистване на ресурси — основен сценарий: отмяна на таймери, затваряне на връзки, запазване на състояние.
  • Аналитика — чрез onDisappear се изпращат събития screen close, dwell time, session end.
  • Липса на гаранция — onDisappear не се извиква при аварийно прекратяване на приложението.

Какво е .onDisappear?

.onDisappear — модификатор View в SwiftUI, който приема затваряне Void и го изпълнява при премахване на View от йерархията. Заедно с .onAppear формира пълния жизнен цикъл на екрана: появяване — работа — изчезване. Apple представи onDisappear заедно с пускането на SwiftUI в iOS 13 като аналог на viewDidDisappear от UIKit.

Синтактично .onDisappear е идентичен с onAppear: модифицира всяко View и прикрепя затваряне, което се извиква от SwiftUI компонента при премахване на изгледа. За разлика от UIKit, където viewDidDisappear се активира едва след завършване на анимацията на прехода, onDisappear в SwiftUI може да бъде извикан преди завършване на анимацията — в момента, когато View е маркиран за премахване.

Синтаксис на onDisappear

Основен синтаксис на onDisappear е също толкова сбит, колкото на onAppear. Модификаторът не приема допълнителни параметри — само затваряне, което се изпълнява синхронно в основната нишка.

swift
struct DetailView: View {
    var body: some View {
        Text("Екран за детайли")
            .onDisappear {
                print("DetailView изчезна от екрана")
            }
    }
}

Как работи .onDisappear

Механизъм на извикване на onDisappear е противоположен на onAppear: първо дъщерните елементи получават onDisappear, после родителят. Това правило child-first гарантира, че ресурсите на дъщерните елементи се освобождават преди освобождаването на ресурсите на родителя. Ако дъщерен елемент зависи от данните на родителя, той трябва да може да завърши правилно без контекста на родителя.

SwiftUI извиква onDisappear в момента, когато View се изключва от графа за рендериране. Задействащ механизъм може да бъде: pop от NavigationStack, превключване на TabView, dismiss на модален прозорец (sheet/fullScreenCover), промяна на условно показвано View (if/switch). В List и ScrollView onDisappear се извиква при скролване на клетка извън prefetch буфера.

Ред на извикване child-first

Ред child-first означава, че ако в VStack има три дъщерни View, onDisappear ще бъде извикан първо за всяко дете в обратен ред, после за родителя. Това е критично за правилно почистване: дъщерните таймери се отменят, преди родителският ViewModel да освободи споделените ресурси.

swift
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

Сценарии на извикване на onDisappear зависят от типа на контейнера. В NavigationStack onDisappear се задейства при pop-to-root, обикновен pop-back или премахване на екран чрез жест на плъзгане (interactivePopGestureRecognizer). В TabView превключването на раздел задейства onDisappear за скрития и onAppear за показания — и двата модификатора се задействат почти едновременно.

В Sheet и fullScreenCover onDisappear се извиква при програмно dismiss (чрез @Environment(\.dismiss)) или жест на плъзгане надолу. Важна особеност: ако sheet е бил отворен, но потребителят е превключил приложението, onDisappear НЕ се извиква до действителното затваряне.

СценарийonDisappear се извикваЗабележка
Pop в NavigationStackДаВеднага след анимация
Превключване на раздел TabViewДаТекущ раздел
Dismiss на sheetДаПреди завършване на анимация
Скрол в ListДаКлетка извън prefetch зоната
Свиване на приложениетоНеНяма гаранция за извикване
Crash/watchdog killНеНе се извиква

Примери за .onDisappear

Основни сценарии на onDisappear — почистване на ресурси, запазване на състояние и проследяване. За разлика от onAppear, задачите на onDisappear се изпълняват при изход и не изискват проверка за дублиране, тъй като View изчезва еднократно.

Запазване на състояние и отмяна на таймери

Запазване на състояние в onDisappear е особено полезно за формуляри, където данните трябва да бъдат запазени при напускане на екрана. Таймерите и Combine абонаментите се отменят в onDisappear, за да се избегнат изтичания на памет при връщане към екрана.

swift
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, причинявайки runtime предупреждение.

Проследяване на време на екрана

Продължителност на престой на екрана — класически сценарий за проследяване. Записваме времето в onAppear, изчисляваме разликата в onDisappear и изпращаме аналитично събитие с продължителността на сесията.

swift
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 vs .onAppear — разлики

Ключова разлика на onDisappear от onAppear — ред на извикване и гаранции за задействане. onAppear се извиква при добавяне на View в йерархията и има свойството да бъде извикан отново при пресъздаване. onDisappear се извиква при премахване и гарантирано работи само при нормално затваряне, но не и при аварийни сценарии.

Според WWDC 2024, Apple препоръчва да се разглежда onDisappear като точка за почистване, а не като точка за запис на данни. Критично важните данни (плащания, регистрации) трябва да се запазват в реално време, а не в момента на изчезване на View, тъй като onDisappear не гарантира извикване при свиване на приложението.

Характеристика.onAppear.onDisappear
Момент на извикванеДобавяне на View в йерархияПремахване на View от йерархия
РедParent-firstChild-first
ГаранцияВисокаНе при аварийно прекратяване
Основна задачаИнициализацияПочистване
Повторно извикванеПри пресъздаване на ViewВеднъж на изчезване

Препоръка: използвайте onDisappear само за некритично почистване и проследяване. За запазване на данни използвайте scenePhase или уведомления applicationWillTerminate в AppDelegate.

Типични сценарии на приложение

Сценарий 1: зареждане подобно на YouTube. На екрана с детайли на видео onDisappear запазва позицията на възпроизвеждане в UserDefaults. При повторно отваряне onAppear възстановява позицията от UserDefaults, създавайки ефект на непрекъснато гледане.

Сценарий 2: отмяна на Combine абонамент. Ако ViewModel използва Combine publisher-и, onDisappear отменя абонамента чрез cancellable?.cancel(). Това предотвратява актуализиране на UI след напускане на екрана, което е особено важно за списъци с пагинация и заявки за търсене.

Сценарий 3: затваряне на WebSocket. В приложения с real-time връзки (месинджъри, потоци от котировки) onDisappear затваря WebSocket връзката за пестене на батерия и трафик. Повторното отваряне става в onAppear при връщане на екрана.

Затваряне на WebSocket в onDisappear

WebSocket — типичен ресурс, който трябва да се затваря при напускане на екрана. В примера по-долу onDisappear прекъсва socket-а, а onAppear го свързва отново, което дава значителна икономия на трафик за приложения с множество екрани.

swift
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 да не се извика?

Да, .onDisappear не гарантира извикване при аварийно прекратяване на приложението (crash, watchdog kill), свиване на приложението без затваряне на екрана или системен сценарий, при който приложението се унищожава на заден план. За критично важни данни използвайте scenePhase или applicationWillTerminate.

Как да различим .onDisappear от .scenePhase?

.onDisappear се задейства при премахване на View от йерархията — локално обратно извикване за конкретен екран. .scenePhase (чрез @Environment(\.scenePhase)) се задейства при промяна на състоянието на цялото приложение: active, inactive, background. За проследяване на време на екрана използвайте onAppear+onDisappear, за запазване на глобално състояние — scenePhase.

Защо .onDisappear се извиква няколко пъти подред?

Причината — бързо превключване между раздели или многократен push/pop на един и същ екран. SwiftUI може да създаде нова инстанция на View, да премахне старата, да създаде отново — всеки път задействайки onDisappear и onAppear. Проверете дали използвате .id(), .equatable() или дали пресъздавате View в body на родителя.

Работи ли .onDisappear в SwiftUI на macOS?

Да, .onDisappear се поддържа напълно на macOS (10.15+) със същото поведение: извиква се при затваряне на прозорец, премахване на split view панел или dismiss на модален прозорец. На macOS onDisappear се извиква също при скриване на прозорец (hide), а не само при затваряне, което е важно за macOS приложения.

Как да отменим URLSessionTask в .onDisappear?

Запазете референция към URLSessionTask в @State и извикайте task.cancel() в onDisappear. Алтернативно използвайте модификатора .task, който автоматично отменя async операцията при изчезване на View. .task е предпочитан за всички async операции, включително URLSession.

Резюме

  • .onDisappear — сдвоен модификатор на onAppear за почистване на ресурси и проследяване при изчезване на View.
  • Child-first ред — дъщерните View получават onDisappear по-рано от родителя за правилна верига на освобождаване.
  • Няма гаранция за извикване — при crash, свиване или системно прекратяване onDisappear може да не работи.
  • Основни сценарии — отмяна на таймери, затваряне на WebSocket, запазване на състояние, проследяване на продължителност.
  • Аналитика — onDisappear изпраща събития screen_close с изчисляване на dwell time чрез Date().timeIntervalSince.
  • За async операции — използвайте .task вместо onDisappear с Task {}, тъй като .task автоматично отменя заявките.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също