viewDidDisappear — е метод на жизнения цикъл на UIViewController, който се извиква веднага след пълното изчезване на изгледа (view) от екрана на iOS устройство. Разработчиците го използват за спиране на анимации, освобождаване на RAM памет, отписване от известия и запазване на текущото състояние. Според Apple Developer Documentation (2025), правилната имплементация на този метод предотвратява до 40% от изтичанията на памет в приложения с активна навигация. Без него фоновите процеси могат да продължат работа, изразходвайки ресурси на батерията и процесора. Правилното използване на viewDidDisappear е едно от ключовите умения на iOS разработчика, което пряко влияе върху производителността и стабилността на приложението.
Основни точки
viewDidDisappear — е метод-кука на суперкласа UIViewController, който системата извиква, след като изгледът (view) бъде напълно премахнат от йерархията на прозорците на екрана. Той е част от стандартния жизнен цикъл на изглед в UIKit и предоставя на разработчика точка за изпълнение на завършващи операции.
Методът е деклариран в протокола UIViewController и е достъпен за замяна във всички подкласове. Сигнатурата на метода: override func viewDidDisappear(_ animated: Bool). Параметърът animated указва дали преходът е бил придружен от анимация. Това позволява разграничаване на програмни и анимирани преходи за по-прецизен контрол на поведението.
За разлика от viewWillDisappear, който се извиква преди започване на анимацията, viewDidDisappear гарантира, че изгледът вече не е видим за потребителя. Това е критично за операции, които трябва да се изпълнят само след пълно скриване на интерфейса — например скриване на целоекранни overlay елементи или завършване на запис на видео.
Методът е дефиниран в базовия клас UIViewController и има следната сигнатура:
import UIKit
class MyViewController: UIViewController {
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// Освобождаване на ресурси и отписване
}
}
Задължителното извикване на super.viewDidDisappear(animated) в първия ред на имплементацията — това е изискване на UIKit. Без него суперкласът не може да завърши правилно вътрешните процеси, свързани с показването на изгледа. Игнорирането на това правило води до непредвидимо поведение на навигацията и потенциални сривове.
Пълният жизнен цикъл на UIViewController се състои от шест ключови метода, всеки от които отговаря за определена фаза на съществуване на изгледа. viewDidDisappear завършва последователността на скриване, следвайки viewWillDisappear. Важно е да се разбере редът на извикване на всички методи, за да се разпредели правилно инициализацията и освобождаването на ресурси.
Редът при появяване на изгледа: viewDidLoad → viewWillAppear → viewDidAppear. При скриване: viewWillDisappear → viewDidDisappear. Крайната фаза — deinit, който се извиква при унищожаване на обекта UIViewController. Тези шест метода формират пълен цикъл, гарантиращ предвидимо управление на състоянието.
| Метод | Момент на извикване | Типично приложение |
|---|---|---|
| viewDidLoad | След зареждане на изгледа в паметта | Начална настройка на UI, абонамент за данни |
| viewWillAppear | Преди появяване на изгледа на екрана | Актуализиране на данни преди показване |
| viewDidAppear | След появяване на изгледа на екрана | Стартиране на анимации, начало на анимация |
| viewWillDisappear | Преди изчезване на изгледа | Запазване на въведени данни, отмяна на операции |
| viewDidDisappear | След изчезване на изгледа | Освобождаване на ресурси, отписване от известия |
| deinit | При унищожаване на обекта | Крайно почистване, освобождаване на силни референции |
Всеки от тези методи се извиква точно веднъж за съответния преход. Изключение — viewDidLoad, който може да бъде извикан отново, ако ViewController бъде изтоварен от паметта поради липса на ресурси и след това възстановен. В такъв случай viewDidDisappear ще предшества новото viewDidLoad.
Параметърът animated в сигнатурата на метода указва дали преходът е бил анимиран. Това е полезно за разграничаване на програмни преходи без анимация (например при задаване на rootViewController) и анимирани преходи, инициирани от потребителя. Ако стойността е false, възможно е контролерът да е бил принудително скрит от системата — в този случай някои зависими от времето операции може да са неактуални.
Системата извиква viewDidDisappear точно в два сценария: когато ViewController се премахва от стека за навигация и когато се покрива от друг контролер. И в двата случая методът сигнализира, че изгледът вече не е видим за потребителя и разработчикът трябва да освободи ресурси, които не са необходими на фона. Разбирането на тези сценарии предотвратява грешни предположения за състоянието на приложението.
Първи сценарий — pop от UINavigationController. Когато потребителят натисне бутона „Назад”, се извиква popViewController: animated. Текущият контролер получава viewDidDisappear, а след това, ако няма повече силни референции към него, deinit. Втори сценарий — present/dismiss. При модално показване на нов контролер presentingViewController получава viewDidDisappear. При dismiss този метод се извиква при контролера, който е бил показан модално.
Трети, по-малко очевиден сценарий — добавяне на child ViewController. Ако към контейнерен контролер (например UIPageViewController или UITabBarController) се добави нов дъщерен контролер, активният дъщерен контролер получава viewDidDisappear. Това е критично за приложения с раздели или карусел от страници — всяка смяна на раздел трябва правилно да спира работата на неактивния екран.
Съществува важно изключение: ако UIViewController се показва в модален прозорец и потребителят го затвори интерактивно чрез плъзгане надолу, системата може да не извика viewDidDisappear при непълно плъзгане. Това поведение се появи в iOS 13 заедно с интерактивния dismiss. Разработчиците трябва да управляват състоянието чрез UIAdaptivePresentationControllerDelegate и метода didDismiss за гарантирано получаване на събитието.
Друга особеност — предупреждения за памет. При недостиг на памет системата може да изтовари изгледа на контролер, който не се показва на екрана. В този случай viewDidDisappear обикновено се извиква преди изтоварването, но разработчикът трябва да дублира критично важните операции за освобождаване в didReceiveMemoryWarning за сигурност. Такъв подход предотвратява загуба на данни в екстремни сценарии.
viewDidDisappear се използва за три основни категории операции: спиране на активности, освобождаване на ресурси и запазване на състояние. Всяка категория има своите най-добри практики, разработени от общността на iOS разработчиците. Нека разгледаме най-честите сценарии с примери за имплементация.
Типична грешка — абониране за известия в viewDidLoad и никога не се отписване. Това води до извикване на handler на унищожен обект, което причинява срив. Правилният подход — абониране в viewWillAppear и отписване в viewDidDisappear, което гарантира, че абонаментът е активен само докато контролерът се показва на екрана.
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)
}
Този модел гарантира, че handler на известия е активен само когато контролерът е видим на екрана. При преминаване към друг екран всички абонаменти автоматично се премахват, а при връщане се възстановяват. Това повишава надеждността на приложението и елиминира клас грешки, свързани с известия.
Нека разгледаме два практически примера за използване на viewDidDisappear в реални проекти. Първият пример демонстрира спиране на таймер при скриване на екрана, вторият — правилно завършване на наблюдение на клавиатурата. И двата примера следват принципа на освобождаване на ресурси при неактивност на контролера.
Ако на екрана работи Timer за актуализиране на UI (например обратно броене или карусел), той трябва да бъде спрян при скриване на контролера. Продължаването на работата на таймера на фона не само изразходва ресурси на процесора, но може също да причини изключение при опит за актуализиране на невидим 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
}
}
В много приложения AVPlayer възпроизвежда видео във вграден плейър. Ако потребителят премине към друг екран, видеото трябва автоматично да се паузира. Имплементацията в viewDidDisappear гарантира, че паузата настъпва след пълно скриване на екрана — това предотвратява мигане на черен кадър при прехода.
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if player().timeControlStatus == .playing {
player().pause()
playerLayer().removeFromSuperlayer()
}
player = nil
}
Зануляването на променливата player след пауза допълнително освобождава паметта, заета от видео буферите. Този подход е особено важен за приложения с дълги видеа, където буферът може да заема десетки мегабайта. Комбинирането на пауза със зануляване на референции минимизира отпечатъка на приложението на фона.
viewDidDisappear често се бърка с viewWillDisappear и deinit, но всеки от тези методи има своя зона на отговорност. Разбирането на границите между тях е ключът към стабилна архитектура на iOS приложение. Неправилното използване може да доведе до двойно освобождаване на ресурси или, обратно, до тяхното изтичане.
Основната разлика на viewDidDisappear от viewWillDisappear — моментът на извикване. viewWillDisappear се извиква, когато изгледът е все още видим, но вече се подготвя за изчезване. Това е подходящо за запазване на видими данни (текст в полета за въвеждане). viewDidDisappear се извиква след завършване на анимацията, когато изгледът е гарантирано невидим — идеално за освобождаване на ресурси, несвързани с визуалното състояние.
deinit, за разлика от viewDidDisappear, се извиква само при унищожаване на обекта UIViewController в паметта. Ако контролерът е просто скрит (например покрит от модален прозорец), deinit не се извиква. В тази ситуация viewDidDisappear е единствената точка за изпълнение на завършващи операции. Пълното освобождаване на ресурси трябва да се случи в deinit, но viewDidDisappear отговаря за временно освобождаване до повторното появяване.
При разработка с SwiftUI методът viewDidDisappear не се прилага — заменя се от модификатора .onDisappear, който работи по подобен начин. В SwiftUI обаче липсва пряк контрол над жизнения цикъл и разработчиците разчитат на Combine и State обекти за управление на ресурси. За UIKit приложения viewDidDisappear остава основният инструмент за управление на скриване на екрана.
Дори опитни iOS разработчици допускат грешки в работата с viewDidDisappear. Нека разгледаме петте най-чести проблема и начини за предотвратяването им. Познаването на тези анти-модели помага да се избегнат трудно откриваеми грешки, свързани с жизнения цикъл на контролерите.
Особено внимание изисква нишковата безопасност. Ако viewDidDisappear се извиква на основната нишка (което UIKit гарантира), но освобождаването на ресурси включва асинхронни операции, трябва да се синхронизира достъпът до споделени данни. Използването на DispatchQueue.main.async вътре в viewDidDisappear за актуализиране на UI след завършване на асинхронна задача — често срещан, но правилен подход.
Друг важен анти-модел — извикване на делегатски методи вътре в viewDidDisappear, които могат да инициират нов преход или модално показване. Това създава цикъл, в който viewDidDisappear може да бъде извикан отново преди завършване на първото извикване. Apple препоръчва да се избягват модални показвания вътре в методите на жизнения цикъл, като се изнасят в отделни обработчици на събития.
Често задавани въпроси
viewWillDisappear се извиква преди започване на анимацията на скриване, когато изгледът е все още видим. viewDidDisappear — след пълно изчезване на изгледа. За запазване на данни използвайте viewWillDisappear, за освобождаване на ресурси — viewDidDisappear.
Да, извикването на super.viewDidDisappear(animated) е задължително. UIKit използва този метод за вътрешни уведомления и завършване на състоянието на прехода. Без извикване на super са възможни сривове в UINavigationController и UITabBarController.
Да, при интерактивен dismiss в iOS 13+ (плъзгане надолу) методът може да не се извика, ако жестът не е завършен. За гарантирано получаване на събитието използвайте делегата UIAdaptivePresentationControllerDelegate и метода presentationControllerDidDismiss.
deinit се извиква само при унищожаване на обекта, докато viewDidDisappear се извиква при всяко скриване. За освобождаване на ресурси при всеки преход (например отписване от известия) използвайте viewDidDisappear. За крайно почистване при премахване на контролера — deinit.
В SwiftUI вместо viewDidDisappear се използва модификаторът .onDisappear { }. Той се извиква при скриване на изгледа от йерархията. За разлика от UIKit, SwiftUI не гарантира извикване на onDisappear във всички сценарии по време на анимации.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също