viewWillDisappear — це метод UIViewController, який UIKit викликає безпосередньо перед тим, як екран починає зникати з дисплея користувача. Згідно з Apple Developer Documentation, цей метод отримує параметр animated і спрацьовує при push, pop, present, dismiss та перемиканні вкладок. viewWillDisappear — основне місце для збереження стану та коректного очищення ресурсів.
Головне
viewWillDisappear — це метод UIViewController, який UIKit викликає безпосередньо перед тим, як View контролера починає зникати з екрану. У цей момент екран ще видний користувачеві, але перехід вже ініційовано: NavigationController почав анімацію push/pop, модальне вікно почало закриватися, або TabBar почав перемикатися на іншу вкладку. Розробник перевизначає цей метод для виконання операцій, які потребують, щоб екран ще був доступний, але вже готується до приховування.
На відміну від viewDidDisappear, який спрацьовує після того, як екран приховано, viewWillDisappear надає останню можливість зберегти дані та звільнити ресурси, поки користувач ще бачить інтерфейс. Це критично важливо для UX — збереження чорновика або зупинка таймера мають відбуватися до того, як користувач перейде на інший екран.
Метод приймає параметр animated, який вказує, чи відбувається зникнення з анімацією. Значення true означає, що UIKit виконує анімований перехід, false — екран зникає миттєво, наприклад при dismiss без анімації або програмному видаленні з ієрархії.
viewWillDisappear викликається в усіх сценаріях, коли поточний екран перестає бути активним. Розглянемо основні випадки, характерні для iOS-розробки.
Коли UINavigationController виконує push нового контролера, на поточному викликається viewWillDisappear на початку анімації переходу. У цей момент поточний екран ще видний під новим контролером, що насувається зверху. Це стандартний сценарій, при якому viewWillDisappear спрацьовує з animated = true.
Коли користувач натискає кнопку назад або виконує інтерактивний свайп назад, на поточному контролері викликається viewWillDisappear. При інтерактивному жесті цей виклик може бути скасований, якщо користувач передумає та поверне екран на місце. Це важлива особливість, яку потрібно враховувати при проектуванні збереження стану.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
saveDraftData()
NotificationCenter.default.removeObserver(self)
}
При закритті модального вікна viewWillDisappear викликається на закриваному контролері на початку анімації dismiss. У цей момент можна передати результати назад через делегат або замикання, оскільки контролер, який представив модальне вікно, ще не отримав контроль.
UITabBarController викликає viewWillDisappear на контролері вкладки, яку залишають, одразу після того, як користувач торкнув іншої вкладки. Якщо на поточній вкладці є активні процеси — відтворення медіа, завантаження файлу, таймер — їх припиняють або зупиняють тут.
viewWillDisappear вирішує конкретні завдання з управління ресурсами та станом. Розглянемо ключові сценарії з прикладами коду.
Найважливіше завдання viewWillDisappear — збереження даних, які користувач ввів або змінив на поточному екрані. Чорновики повідомлень, відредаговані поля форми, вибрані налаштування — усе це має бути збережено до того, як екран зникне. Використовуйте Core Data, UserDefaults або файлове сховище для персистентності.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
guard hasUnsavedChanges else { return }
draftStorage.save(currentDraft)
}
NotificationCenter, KVO та Combine паблішери, на які ви підписались у viewWillAppear або viewDidLoad, мають бути скасовані в viewWillDisappear. Якщо цього не зробити, сповіщення будуть надходити на прихований екран, спричиняючи оновлення інтерфейсу, які користувач не бачить, або — ще гірше — аварії через доступ до вже деалокованих об’єктів.
Анімації UIView, запущені в viewDidAppear, та таймери, що працюють через Timer або DispatchSource, мають бути зупинені в viewWillDisappear. Продовжування анімацій на прихованому екрані витрачає GPU та батарею без будь-якої користі для користувача. Зупиняйте їх явно, викликаючи invalidate у таймерів та removeAllAnimations у шарів.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
countdownTimer?.invalidate()
countdownTimer = nil
loadingIndicator.layer.removeAllAnimations()
}
Якщо контролер відкривався для отримання результату — вибір елемента, введення тексту, підтвердження дії — viewWillDisappear останній момент, коли оригінальний контролер ще існує в стеку та може приймати дані. Викликайте делегат або замикання до того, як спрацює deinit.
Надійне збереження стану екрану — одне з найскладніших завдань в iOS-розробці. viewWillDisappear — важливий, але не єдиний елемент стратегії. Розглянемо комплексний підхід.
Рівень 1 — збереження в viewWillDisappear. Швидке збереження легких даних, які мають бути доступні одразу після повернення. Підходить для UI-стану: позиція прокрутки, вибраний сегмент, текст у полях вводу. Проблема: при скасованому інтерактивному pop-жесті збереження відбувається, хоча користувач залишився на екрані — дані перезаписуються без потреби.
Рівень 2 — збереження в viewDidDisappear. Дублює збереження з першого рівня, але спрацьовує тільки після того, як екран гарантовано приховано. Це страховка від скасованих жестів. Однак якщо ви вже відписалися від сповіщень в viewWillDisappear, viewDidDisappear може не мати доступу до деяких даних.
Рівень 3 — збереження через сповіщення додатка. UIApplication.willResignActiveNotification та UIApplication.didEnterBackgroundNotification перехоплюють згортання додатка. Якщо користувач згорнув додаток, viewWillDisappear міг не викликатися — але збереження через ці сповіщення гарантує цілісність даних при завершенні сесії.
| Рівень | Метод/Сповіщення | Надійність | Використання |
|---|---|---|---|
| 1 | viewWillDisappear | Висока | UI-стан, чорновики |
| 2 | viewDidDisappear | Дуже висока | Критичні дані |
| 3 | willResignActive | Максимальна | При згортанні |
Рекомендація: використовуйте комбінацію всіх трьох рівнів для критичних користувацьких даних. Для некритичного стану достатньо першого рівня. Важливо не перезберігати ті самі дані багаторазово — використовуйте прапорець dirty, що вказує, що дані змінилися з останнього збереження.
Особливу увагу варто приділити стратегії для CRUD-екранів, де користувач вводить дані. На таких екранах не рекомендується зберігати кожен натиск клавіші в viewWillDisappear — це надлишок. Використовуйте автозбереження з затримкою (debounce) через Timer, а viewWillDisappear застосовуйте лише для фінального форсованого збереження, якщо є незбережені зміни. Такий підхід балансує продуктивність та цілісність даних.
Для додатків, що використовують Core Data, додатковим заходом є виклик saveContext в viewWillDisappear лише при наявності реальних змін в managed object context. Перевірка context.hasChanges перед збереженням запобігає зайвим записам до постійного сховища та продовжує термін служби батареї пристрою. Комбінуйте цю перевірку з глобальним збереженням в applicationDidEnterBackground.
Неправильне використання viewWillDisappear може призвести до втрати даних, витоків пам’яті та нестабільної поведінки додатка. Розглянемо часті помилки iOS-розробників.
Перша помилка — збереження даних лише в viewWillDisappear. Як обговорювалося вище, при інтерактивному pop-жесті метод викликається, навіть якщо екран не зникла насправді. Якщо збереження має побічні ефекти — відправка даних на сервер, зміна стану — це може призвести до хибних спрацьовувань. Додайте перевірку isBeingDismissed або isMovingFromParent.
Друга помилка — відсутність відписки від NotificationCenter. Це один з найчастіших витоків пам’яті в iOS. Якщо ви підписалися в viewWillAppear на UIResponder.keyboardWillShowNotification, але не відписалися в viewWillDisappear, замикання продовжує викликатися. При deinit контролера замикання посилатиметься на вже деалокований об’єкт — аварія додатка гарантована.
Третя помилка — виконання важких синхронних операцій. Збереження великого обсягу даних, запис в Core Data або файлову систему в viewWillDisappear блокує main thread. Якщо операція триває довше, ніж анімація переходу, UIKit призупиняє потік і інтерфейс зависає. Виносьте важкі збереження в фонові черги.
Четверта помилка — забули викликати super. Невиклик super.viewWillDisappear може порушити роботу UINavigationController та UITabBarController, які використовують цей метод для своїх внутрішніх станів. Завжди викликайте super першим або останнім, дотримуючись документації Apple.
Ця проблема погіршується на iOS з активною багатозадачністю та перемиканнями між додатками. П’ята помилка — використання DispatchQueue.main.async після збереження в viewWillDisappear. Якщо ви асинхронно відправляєте блок у головну чергу після виклику super.viewWillDisappear, немає гарантії, що контролер ще існує на момент виконання блока. Завжди використовуйте слабкі посилання [weak self] всередині замикань для запобігання доступу до деалокованої пам’яті та аварій додатка.
Часто задавані питання
viewWillDisappear викликається на початку зникнення, коли екран ще видний. viewDidDisappear — після того, як екран повністю приховано та анімація завершена.
Використовуйте viewDidDisappear для підтвердження збереження або перевіряйте властивості isMovingFromParent та isBeingDismissed всередині viewWillDisappear, щоб визначити, чи зникне екран насправді.
Так, обов’язково, якщо ви використовуєте блоки або селектори з self. ARC не керує підписками на NotificationCenter. В iOS 9+ для блоків використовуйте слабке посилання та відписуйтесь в viewWillDisappear.
Ніяк — force quit не викликає методи Lifecycle. Для гарантованого збереження при завершенні додатка використовуйте UIApplication.willTerminateNotification або зберігайте дані в реальному часі в міру їх зміни.
Так, при інтерактивному pop-жесті UIKit викликає viewWillDisappear одразу після початку жесту. Якщо користувач скасовує жест, екран залишається видним, але метод вже спрацював. Завжди перевіряйте isMovingFromParent.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також