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 — 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.
Metoda jest zdefiniowana w klasie bazowej UIViewController i ma następującą sygnaturę:
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.
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: viewDidLoad → viewWillAppear → viewDidAppear. Przy ukrywaniu: viewWillDisappear → viewDidDisappear. 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.
| Metoda | Moment wywołania | Typowe zastosowanie |
|---|---|---|
| viewDidLoad | Po załadowaniu widoku do pamięci | Początkowa konfiguracja UI, subskrypcja danych |
| viewWillAppear | Przed pojawieniem się widoku na ekranie | Aktualizacja danych przed wyświetleniem |
| viewDidAppear | Po pojawieniu się widoku na ekranie | Uruchamianie animacji, rozpoczęcie animacji |
| viewWillDisappear | Przed zniknięciem widoku | Zapisywanie wprowadzanych danych, anulowanie operacji |
| viewDidDisappear | Po zniknięciu widoku | Zwalnianie zasobów, wypisywanie się z powiadomień |
| deinit | Przy niszczeniu obiektu | Koń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.
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.
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.
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.
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.
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.
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.
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.
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.
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
}
}
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.
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 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ę.
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.
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.
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
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.
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.
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.
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.
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
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.
Przeczytaj również