viewDidDisappear: istota metody, cykl życia UIViewController i kiedy jest wywoływany

Autor: IT Sectr Opublikowano: 2026-03-05 Czas czytania: 9 min

viewDidDisappear — to metoda cyklu życia UIViewController, która jest wywoływana natychmiast po całkowitym zniknięciu widoku z ekranu urządzenia iOS. Programiści używają jej do zatrzymywania animacji, zwalniania pamięci RAM, wypisywania się z powiadomień i zapisywania bieżącego stanu. Według Apple Developer Documentation (2025), prawidłowa implementacja tej metody zapobiega do 40% wycieków pamięci w aplikacjach z aktywną nawigacją. Bez niej procesy w tle mogą kontynuować działanie, zużywając zasoby baterii i procesora. Prawidłowe użycie viewDidDisappear to jedna z kluczowych umiejętności iOS-developera, która bezpośrednio wpływa na wydajność i stabilność aplikacji.

Najważniejsze

  • viewDidDisappear — końcowa metoda cyklu życia, wywoływana po zniknięciu widoku z ekranu
  • Służy do zwalniania zasobów: zatrzymywanie timerów, ukrywanie wskaźników ładowania
  • Obowiązkowa do wypisywania się z NotificationCenter i obserwacji KVO w celu uniknięcia wycieków
  • Różni się od viewWillDisappear tym, że jest wywoływana po zakończeniu animacji przejścia
  • Nie zastępuje deinit — deinit odpowiada za końcowe zniszczenie obiektu

Czym jest viewDidDisappear?

viewDidDisappear — to metoda-hak nadklasy UIViewController, którą system wywołuje po tym, jak widok (view) został całkowicie usunięty z hierarchii okien na ekranie. Jest częścią standardowego cyklu życia widoku w UIKit i zapewnia programiście punkt do wykonywania operacji kończących.

Metoda jest zadeklarowana w protokole UIViewController i dostępna do nadpisania we wszystkich podklasach. Sygnatura metody: override func viewDidDisappear(_ animated: Bool). Parametr animated wskazuje, czy przejściu towarzyszyła animacja. Pozwala to rozróżnić programowe i animowane przejścia dla bardziej precyzyjnego sterowania zachowaniem.

W przeciwieństwie do viewWillDisappear, który jest wywoływany przed rozpoczęciem animacji, viewDidDisappear gwarantuje, że widok nie jest już widoczny dla użytkownika. Jest to krytyczne dla operacji, które muszą być wykonane dopiero po całkowitym ukryciu interfejsu — na przykład ukrywanie pełnoekranowych elementów overlay lub zakończenie nagrywania wideo.

Sygnatura i deklaracja

Metoda jest zdefiniowana w klasie bazowej UIViewController i ma następującą sygnaturę:

swift
import UIKit

class MyViewController: UIViewController {
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        // Zwalnianie zasobów i wypisywanie się
    }
}

Obowiązkowe wywołanie super.viewDidDisappear(animated) w pierwszej linii implementacji — to wymóg UIKit. Bez niego superklasa nie będzie mogła poprawnie zakończyć wewnętrznych procesów związanych z wyświetlaniem widoku. Ignorowanie tej reguły prowadzi do nieprzewidywalnego zachowania nawigacji i potencjalnych awarii.

Miejsce viewDidDisappear w cyklu życia UIViewController

Pełny cykl życia UIViewController składa się z sześciu kluczowych metod, z których każda odpowiada za określoną fazę istnienia widoku. viewDidDisappear kończy sekwencję ukrywania, następując po viewWillDisappear. Ważne jest zrozumienie kolejności wywoływania wszystkich metod, aby prawidłowo rozdzielać inicjalizację i zwalnianie zasobów.

Kolejność przy pojawianiu się widoku: viewDidLoadviewWillAppearviewDidAppear. Przy ukrywaniu: viewWillDisappearviewDidDisappear. Faza końcowa — deinit, który jest wywoływany przy niszczeniu obiektu UIViewController. Te sześć metod tworzy pełny cykl, gwarantujący przewidywalne zarządzanie stanem.

MetodaMoment wywołaniaTypowe zastosowanie
viewDidLoadPo załadowaniu widoku do pamięciPoczątkowa konfiguracja UI, subskrypcja danych
viewWillAppearPrzed pojawieniem się widoku na ekranieAktualizacja danych przed wyświetleniem
viewDidAppearPo pojawieniu się widoku na ekranieUruchamianie animacji, rozpoczęcie animacji
viewWillDisappearPrzed zniknięciem widokuZapisywanie wprowadzanych danych, anulowanie operacji
viewDidDisappearPo zniknięciu widokuZwalnianie zasobów, wypisywanie się z powiadomień
deinitPrzy niszczeniu obiektuKońcowe czyszczenie, zwalnianie silnych referencji

Każda z tych metod jest wywoływana dokładnie raz na odpowiednie przejście. Wyjątkiem jest viewDidLoad, który może być wywołany ponownie, jeśli ViewController został zwolniony z pamięci przy braku zasobów, a następnie przywrócony. W takim przypadku viewDidDisappear będzie poprzedzać ponowne viewDidLoad.

Związek z animacją przejścia

Parametr animated w sygnaturze metody informuje, czy przejście było animowane. Jest to przydatne do rozróżniania programowych przejść bez animacji (na przykład przy ustawianiu rootViewController) i animowanych przejść inicjowanych przez użytkownika. Jeśli wartość to false, możliwe, że kontroler został ukryty przez system przymusowo — w takim przypadku niektóre operacje zależne od czasu mogą być nieaktualne.

Kiedy wywoływane jest viewDidDisappear

System wywołuje viewDidDisappear dokładnie w dwóch scenariuszach: gdy ViewController jest usuwany ze stosu nawigacji i gdy jest przykrywany przez inny kontroler. W obu przypadkach metoda sygnalizuje, że widok nie jest już widoczny dla użytkownika, a programista powinien zwolnić zasoby, które nie są potrzebne w tle. Zrozumienie tych scenariuszy zapobiega błędnym założeniom o stanie aplikacji.

Pierwszy scenariusz — pop z UINavigationController. Gdy użytkownik naciska przycisk „Wstecz”, wywoływane jest popViewController: animated. Bieżący kontroler otrzymuje viewDidDisappear, a następnie, jeśli nie ma do niego więcej silnych referencji, deinit. Drugi scenariusz — present/dismiss. Przy modalnym wyświetleniu nowego kontrolera presentingViewController otrzymuje viewDidDisappear. Przy dismiss ta metoda jest wywoływana u kontrolera, który był wyświetlony modalnie.

Trzeci, mniej oczywisty scenariusz — dodawanie child ViewController. Jeśli do kontrolera kontenerowego (na przykład UIPageViewController lub UITabBarController) dodawany jest nowy kontroler potomny, aktywny kontroler potomny otrzymuje viewDidDisappear. Jest to krytyczne dla aplikacji z zakładkami lub karuzelami stron — każda zmiana zakładki musi poprawnie wstrzymywać działanie nieaktywnego ekranu.

Wyjątki i nieoczywiste przypadki

Istnieje ważny wyjątek: jeśli UIViewController jest wyświetlany w oknie modalnym, a użytkownik zamyka go interaktywnie przez przesunięcie w dół, system może nie wywołać viewDidDisappear przy niepełnym przesunięciu. To zachowanie pojawiło się w iOS 13 wraz z interaktywnym dismiss. Programiści powinni obsługiwać stan przez UIAdaptivePresentationControllerDelegate i metodę didDismiss, aby zagwarantować otrzymanie zdarzenia.

Kolejna cecha — memory warnings. Przy braku pamięci system może zwolnić widok kontrolera, który nie jest wyświetlany na ekranie. W takim przypadku viewDidDisappear jest zwykle wywoływane przed zwolnieniem, ale programista powinien powielić krytycznie ważne operacje zwalniania w didReceiveMemoryWarning dla bezpieczeństwa. Takie podejście zapobiega utracie danych w ekstremalnych scenariuszach.

Typowe scenariusze użycia

viewDidDisappear jest używane do trzech głównych kategorii operacji: zatrzymywanie aktywności, zwalnianie zasobów i zapisywanie stanu. Każda kategoria ma swoje best practices wypracowane przez społeczność iOS-developerów. Rozważmy najczęstsze scenariusze z przykładami implementacji.

  • Zatrzymywanie animacji — wywołanie layer.removeAllAnimations() dla CALayer, zatrzymywanie bloków UIView.animate
  • Zwalnianie zasobów — zerowanie dużych obrazów, resetowanie buforowanych danych, zamykanie deskryptorów plików
  • Wypisywanie się z powiadomień — usuwanie obserwatorów z NotificationCenter.default, zatrzymywanie obserwacji KVO
  • Zapisywanie postępu — zapisywanie szkiców w CoreData lub UserDefaults przy zamykaniu ekranu edycji
  • Ukrywanie overlay — usuwanie wskaźników ładowania, podpowiedzi i elementów popover, które nie powinny pozostać po przejściu

Przykład: wypisywanie się z NotificationCenter

Typowy błąd — subskrypcja powiadomień w viewDidLoad i nigdy się z nich nie wypisywać. Prowadzi to do wywołania handlera na zniszczonym obiekcie, co powoduje crash. Prawidłowe podejście — subskrypcja w viewWillAppear i wypisanie się w viewDidDisappear, co gwarantuje aktualność subskrypcji tylko podczas wyświetlania kontrolera na ekranie.

swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(handleKeyboardShow),
        name: UIResponder.keyboardWillShowNotification,
        object: nil
    )
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    NotificationCenter.default.removeObserver(self)
}

Taki wzorzec gwarantuje, że handler powiadomień jest aktywny tylko wtedy, gdy kontroler jest widoczny na ekranie. Przy przejściu na inny ekran wszystkie subskrypcje są automatycznie usuwane, a przy powrocie przywracane. Zwiększa to niezawodność aplikacji i eliminuje klasę błędów związanych z powiadomieniami.

Przykłady kodu w Swift

Rozważmy dwa praktyczne przykłady użycia viewDidDisappear w rzeczywistych projektach. Pierwszy przykład demonstruje zatrzymanie timera przy ukrywaniu ekranu, drugi — poprawne zakończenie obserwacji klawiatury. Oba przykłady są zgodne z zasadą zwalniania zasobów przy nieaktywności kontrolera.

Zatrzymywanie timera

Jeśli na ekranie działa Timer do aktualizacji UI (na przykład odliczanie lub karuzela), należy go zatrzymać przy ukrywaniu kontrolera. Kontynuowanie działania timera w tle nie tylko zużywa zasoby procesora, ale może też spowodować wyjątek przy próbie aktualizacji niewidocznego UI.

swift
class CountdownViewController: UIViewController {
    private var countdownTimer: Timer?
    private var remainingSeconds: Int = 60

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        startTimer()
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        invalidateTimer()
    }

    private func invalidateTimer() {
        countdownTimer()?.invalidate()
        countdownTimer = nil
    }
}

Pauzowanie wideo przy ukrywaniu

W wielu aplikacjach AVPlayer odtwarza wideo we wbudowanym odtwarzaczu. Jeśli użytkownik przechodzi na inny ekran, wideo powinno automatycznie zostać wstrzymane. Implementacja w viewDidDisappear gwarantuje, że pauza następuje po całkowitym ukryciu ekranu — zapobiega to migotaniu czarnej klatki przy przejściu.

swift
override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    if player().timeControlStatus == .playing {
        player().pause()
        playerLayer().removeFromSuperlayer()
    }
    player = nil
}

Zerowanie zmiennej player po pauzie dodatkowo zwalnia pamięć zajętą przez bufory wideo. To podejście jest szczególnie ważne w aplikacjach z długimi filmami, gdzie bufor może zajmować dziesiątki megabajtów. Połączenie pauzy z zerowaniem referencji minimalizuje footprint aplikacji w tle.

viewDidDisappear a inne metody cyklu życia

viewDidDisappear jest często mylone z viewWillDisappear i deinit, jednak każda z tych metod ma swoją strefę odpowiedzialności. Zrozumienie granic między nimi to klucz do stabilnej architektury aplikacji iOS. Nieprawidłowe użycie może prowadzić do podwójnego zwalniania zasobów lub, przeciwnie, do ich wycieku.

Główna różnica viewDidDisappear od viewWillDisappear — moment wywołania. viewWillDisappear jest wywoływane, gdy widok jest jeszcze widoczny, ale już przygotowuje się do zniknięcia. To nadaje się do zapisywania widocznych danych (tekst w polach wprowadzania). viewDidDisappear jest wywoływane po zakończeniu animacji, gdy widok jest gwarantowanie niewidoczny — idealnie nadaje się do zwalniania zasobów niezwiązanych ze stanem wizualnym.

deinit, w przeciwieństwie do viewDidDisappear, jest wywoływane tylko przy niszczeniu obiektu UIViewController w pamięci. Jeśli kontroler jest tylko ukryty (na przykład przykryty oknem modalnym), deinit nie jest wywoływane. W tej sytuacji viewDidDisappear jest jedynym punktem do wykonywania operacji kończących. Całkowite zwalnianie zasobów powinno odbywać się w deinit, ale viewDidDisappear odpowiada za tymczasowe zwolnienie do ponownego pojawienia się.

Kiedy używać której metody

  • viewWillDisappear — zapisywanie wprowadzanych danych, wysyłanie analityki o rozpoczęciu przejścia
  • viewDidDisappear — zatrzymywanie animacji, wypisywanie się z powiadomień, ukrywanie elementów overlay
  • deinit — końcowe zwalnianie dużych zasobów, zamykanie połączeń sieciowych

Przy programowaniu z użyciem SwiftUI metoda viewDidDisappear nie jest stosowana — zastępuje ją modyfikator .onDisappear, który działa w podobny sposób. Jednak w SwiftUI brakuje bezpośredniej kontroli nad cyklem życia, a programiści polegają na Combine i obiektach State do zarządzania zasobami. Dla aplikacji UIKit viewDidDisappear pozostaje głównym narzędziem zarządzania ukrywaniem ekranu.

Typowe błędy przy implementacji

Nawet doświadczeni iOS-developerzy popełniają błędy w pracy z viewDidDisappear. Rozważmy pięć najczęstszych problemów i sposoby ich zapobiegania. Znajomość tych antywzorców pomoże uniknąć trudnych do wyśledzenia błędów związanych z cyklem życia kontrolerów.

  • Pominięcie super.viewDidDisappear — wywołanie super jest obowiązkowe dla poprawnego działania UIKit, jego brak może spowodować naruszenie wewnętrznego stanu kontrolera
  • Ciężkie operacje w viewDidDisappear — synchroniczny zapis dużych danych w viewDidDisappear blokuje główny wątek i pogarsza animację przejścia
  • Zapomniane wypisanie się z powiadomień — jeśli nie wywołamy removeObserver w viewDidDisappear, handler może zadziałać na zombie-obiekcie, powodując EXC_BAD_ACCESS
  • Podwójne wypisanie się — usunięcie obserwatora, który już został usunięty w innym miejscu, prowadzi do wyjątku NSInternalInconsistencyException
  • Zależność od kolejności wywołania — w zagnieżdżonych kontenerach kolejność wywołania viewDidDisappear u child i parent kontrolerów nie jest gwarantowana

Szczególnej uwagi wymaga bezpieczeństwo wątkowe. Jeśli viewDidDisappear jest wywoływane na głównym wątku (co gwarantuje UIKit), ale zwalnianie zasobów obejmuje operacje asynchroniczne, konieczna jest synchronizacja dostępu do współdzielonych danych. Użycie DispatchQueue.main.async wewnątrz viewDidDisappear do aktualizacji UI po zakończeniu zadania asynchronicznego — to powszechne, ale poprawne podejście.

Kolejny ważny antywzorzec — wywoływanie metod delegata wewnątrz viewDidDisappear, które mogą inicjować nowe przejście lub modalne wyświetlenie. Tworzy to cykl, w którym viewDidDisappear może być wywołane ponownie przed zakończeniem pierwszego wywołania. Apple zaleca unikanie modalnych wyświetleń wewnątrz metod cyklu życia, wynosząc je do oddzielnych handlerów zdarzeń.

Często zadawane pytania

Czym viewDidDisappear różni się od viewWillDisappear?

viewWillDisappear jest wywoływane przed rozpoczęciem animacji ukrywania, gdy widok jest jeszcze widoczny. viewDidDisappear — po całkowitym zniknięciu widoku. Do zapisywania danych używaj viewWillDisappear, do zwalniania zasobów — viewDidDisappear.

Czy trzeba wywoływać super.viewDidDisappear?

Tak, wywołanie super.viewDidDisappear(animated) jest obowiązkowe. UIKit używa tej metody do wewnętrznych powiadomień i zakończenia stanu przejścia. Bez wywołania super możliwe są awarie w UINavigationController i UITabBarController.

Czy viewDidDisappear może nie zostać wywołane?

Tak, przy interaktywnym dismiss w iOS 13+ (przesunięcie w dół) metoda może nie zostać wywołana, jeśli gest nie został zakończony. Aby zagwarantować otrzymanie zdarzenia, użyj delegata UIAdaptivePresentationControllerDelegate i metody presentationControllerDidDismiss.

Co jest lepsze: viewDidDisappear czy deinit?

deinit jest wywoływane tylko przy niszczeniu obiektu, a viewDidDisappear przy każdym ukryciu. Do zwalniania zasobów przy każdym przejściu (na przykład wypisywanie się z powiadomień) używaj viewDidDisappear. Do końcowego czyszczenia przy usuwaniu kontrolera — deinit.

Jak działa viewDidDisappear w SwiftUI?

W SwiftUI zamiast viewDidDisappear używany jest modyfikator .onDisappear { }. Jest on wywoływany przy ukrywaniu widoku z hierarchii. W przeciwieństwie do UIKit, SwiftUI nie gwarantuje wywołania onDisappear we wszystkich scenariuszach przy animacjach.

Podsumowanie

  • viewDidDisappear — ostatnia metoda cyklu życia przed ukryciem, wywoływana po zakończeniu animacji przejścia
  • Główne przeznaczenie — zwalnianie zasobów, zatrzymywanie timerów i wypisywanie się z powiadomień
  • Obowiązkowe wywołanie super.viewDidDisappear do poprawnego działania UIKit
  • Różni się od viewWillDisappear momentem wywołania: po animacji, a nie przed nią
  • Nie zastępuje deinit — deinit jest wywoływane przy niszczeniu obiektu, viewDidDisappear przy każdym ukryciu
  • Nie jest używane do ciężkich operacji synchronicznych — blokują one główny wątek i zakłócają animację
  • W iOS 13+ wymagane jest dodatkowe przetwarzanie przez UIAdaptivePresentationControllerDelegate dla gwarantowanego wywołania

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również