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.
Когда пользователь нажимает back button или выполняет интерактивный свайп назад, у текущего контроллера вызывается 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 publishers, на которые вы подписались в viewWillAppear или viewDidLoad, должны быть отменены в viewWillDisappear. Если этого не сделать, уведомления будут приходить на скрытый экран, вызывая обновления UI, которые пользователь не видит, или — хуже — падения из-за обращения к уже освобождённым объектам.
Анимации 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 — это избыточно. Используйте auto-save с задержкой (debounce) через Timer, а viewWillDisappear применяйте только для финального форсированного сохранения, если есть несохранённые изменения. Такой подход балансирует между производительностью и сохранностью данных.
Для приложений с Core Data дополнительной мерой является вызов saveContext в viewWillDisappear только при наличии реальных изменений в managed object context. Проверка context.hasChanges перед сохранением предотвращает лишние записи в persistent store и продлевает срок службы батареи устройства. Комбинируйте эту проверку с глобальным сохранением в 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 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также