viewDidDisappear: суть метода, жизненный цикл UIViewController и когда вызывается

Автор: IT Sectr Опубликовано: 2026-03-05 Время чтения: 9 мин

viewDidDisappear — это метод жизненного цикла UIViewController, который вызывается сразу после полного исчезновения представления с экрана устройства iOS. Разработчики используют его для остановки анимаций, освобождения оперативной памяти, отписки от уведомлений и сохранения текущего состояния. По данным Apple Developer Documentation (2025), корректная реализация этого метода предотвращает до 40% утечек памяти в приложениях с активной навигацией. Без него фоновые процессы могут продолжать работу, consuming ресурсы батареи и процессора. Правильное использование viewDidDisappear — один из ключевых навыков iOS-разработчика, который напрямую влияет на производительность и стабильность приложения.

Главное

  • viewDidDisappear — финальный метод жизненного цикла, вызываемый после исчезновения view с экрана
  • Используется для освобождения ресурсов: остановка таймеров, скрытие индикаторов загрузки
  • Обязателен для отписки от NotificationCenter и KVO-наблюдений во избежание утечек
  • Отличается от viewWillDisappear тем, что вызывается после завершения анимации перехода
  • Не заменяет deinit — deinit отвечает за финальное уничтожение объекта

Что такое viewDidDisappear?

viewDidDisappear — это метод-хук суперкласса UIViewController, который система вызывает после того, как представление (view) полностью удалено из иерархии окон на экране. Он является частью стандартного жизненного цикла view в UIKit и предоставляет разработчику точку для выполнения завершающих операций.

Метод объявлен в протоколе UIViewController и доступен для переопределения во всех подклассах. Сигнатура метода: override func viewDidDisappear(_ animated: Bool). Параметр animated указывает, сопровождался ли переход анимацией. Это позволяет различать программные и анимированные переходы для более точного управления поведением.

В отличие от viewWillDisappear, который вызывается до начала анимации, viewDidDisappear гарантирует, что view уже не видна пользователю. Это критично для операций, которые должны выполняться только после полного скрытия интерфейса — например, скрытие полноэкранных overlay-элементов или завершение записи видео.

Сигнатура и объявление

Метод определён в базовом классе UIViewController и имеет следующую сигнатуру:

swift
import UIKit

class MyViewController: UIViewController {
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        // Освобождение ресурсов и отписка
    }
}

Обязательный вызов super.viewDidDisappear(animated) в первой строке реализации — это требование UIKit. Без него суперкласс не сможет корректно завершить внутренние процессы, связанные с отображением view. Игнорирование этого правила приводит к непредсказуемому поведению навигации и потенциальным сбоям.

Место viewDidDisappear в жизненном цикле UIViewController

Полный жизненный цикл UIViewController состоит из шести ключевых методов, каждый из которых отвечает за определённую фазу существования view. viewDidDisappear завершает последовательность скрытия, следуя за viewWillDisappear. Важно понимать порядок вызова всех методов, чтобы правильно распределять инициализацию и освобождение ресурсов.

Последовательность при появлении view: viewDidLoadviewWillAppearviewDidAppear. При скрытии: viewWillDisappearviewDidDisappear. Завершающая фаза — deinit, который вызывается при уничтожении объекта UIViewController. Эти шесть методов образуют полный цикл, гарантирующий предсказуемое управление состоянием.

МетодМомент вызоваТипичное применение
viewDidLoadПосле загрузки view в памятьНачальная настройка UI, подписка на данные
viewWillAppearПеред появлением view на экранеОбновление данных перед показом
viewDidAppearПосле появления view на экранеЗапуск анимаций, начало анимации
viewWillDisappearПеред исчезновением viewСохранение вводимых данных, отмена операций
viewDidDisappearПосле исчезновения viewОсвобождение ресурсов, отписка от уведомлений
deinitПри уничтожении объектаФинальная очистка, освобождение сильных ссылок

Каждый из этих методов вызывается ровно один раз за соответствующий переход. Исключение — viewDidLoad, который может вызываться повторно, если ViewController был выгружен из памяти при нехватке ресурсов и затем восстановлен. В таком случае viewDidDisappear будет предшествовать повторному viewDidLoad.

Связь с анимацией перехода

Параметр animated в сигнатуре метода сообщает, был ли переход анимирован. Это полезно для различения программных переходов без анимации (например, при установке rootViewController) и анимированных переходов, инициированных пользователем. Если значение false, возможно, контроллер скрыт системой принудительно — в этом случае некоторые операции, зависящие от времени, могут быть неактуальны.

Когда вызывается viewDidDisappear

Система вызывает viewDidDisappear ровно в двух сценариях: когда ViewController удаляется из стека навигации и когда он покрывается другим контроллером. В обоих случаях метод сигнализирует, что view больше не видна пользователю, и разработчик должен освободить ресурсы, которые не нужны в фоне. Понимание этих сценариев предотвращает ошибочные предположения о состоянии приложения.

Первый сценарий — pop из UINavigationController. Когда пользователь нажимает кнопку "Назад", вызывается popViewController: animated. Текущий контроллер получает viewDidDisappear, а затем, если на него больше нет сильных ссылок, deinit. Второй сценарий — present/dismiss. При модальном показе нового контроллера presentingViewController получает viewDidDisappear. При dismiss этот метод вызывается у контроллера, который был показан модально.

Третий, менее очевидный сценарий — добавление child ViewController. Если в контейнерный контроллер (например, UIPageViewController или UITabBarController) добавляется новый дочерний контроллер, активный дочерний контроллер получает viewDidDisappear. Это критично для приложений с вкладками или каруселями страниц — каждая смена tab должна корректно приостанавливать работу неактивного экрана.

Исключения и неочевидные случаи

Существует важное исключение: если UIViewController отображается в модальном окне, и пользователь свайпом вниз закрывает его интерактивно, система может не вызвать viewDidDisappear при неполном свайпе. Это поведение появилось в iOS 13 вместе с интерактивным dismiss. Разработчики должны обрабатывать состояние через UIAdaptivePresentationControllerDelegate и метод didDismiss для гарантированного получения события.

Ещё одна особенность — memory warnings. При нехватке памяти система может выгрузить view контроллера, который не отображается на экране. В этом случае viewDidDisappear обычно вызывается до выгрузки, но разработчику стоит дублировать критически важные операции освобождения в didReceiveMemoryWarning для подстраховки. Такой подход предотвращает потерю данных при экстремальных сценариях.

Типичные сценарии использования

viewDidDisappear применяется для трёх основных категорий операций: остановка активностей, освобождение ресурсов и сохранение состояния. Каждая категория имеет свои best practices, выработанные сообществом iOS-разработчиков. Рассмотрим наиболее частые сценарии с примерами реализации.

  • Остановка анимаций — вызов layer.removeAllAnimations() для CALayer, остановка UIView.animate блоков
  • Освобождение ресурсов — обнуление больших изображений, сброс кэшированных данных, закрытие файловых дескрипторов
  • Отписка от уведомлений — удаление наблюдателей из NotificationCenter.default, остановка KVO-наблюдений
  • Сохранение прогресса — запись черновиков в CoreData или UserDefaults при закрытии экрана редактирования
  • Скрытие overlay — убирание индикаторов загрузки, тултипов и popover-элементов, которые не должны оставаться после перехода

Пример: отписка от NotificationCenter

Типичная ошибка — подписаться на уведомления в viewDidLoad и никогда не отписываться. Это приводит к вызову обработчика на уничтоженном объекте, что вызывает crash. Правильный подход — подписка в viewWillAppear и отписка в viewDidDisappear, что гарантирует актуальность подписки только во время отображения контроллера на экране.

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)
}

Такой паттерн гарантирует, что обработчик уведомлений активен только когда контроллер виден на экране. При переходе на другой экран все подписки автоматически удаляются, а при возврате восстанавливаются. Это повышает надёжность приложения и исключает класс багов, связанных с уведомлениями.

Примеры кода на Swift

Рассмотрим два практических примера использования viewDidDisappear в реальных проектах. Первый пример демонстрирует остановку таймера при скрытии экрана, второй — корректное завершение наблюдения за клавиатурой. Оба примера следуют принципу освобождения ресурсов при неактивности контроллера.

Остановка таймера

Если на экране работает Timer для обновления UI (например, обратный отсчёт или карусель), его необходимо останавливать при скрытии контроллера. Продолжение работы таймера в фоне не только потребляет ресурсы процессора, но и может вызвать исключение при попытке обновить невидимый 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
    }
}

Пауза видео при скрытии

Во многих приложениях AVPlayer воспроизводит видео во встроенном плеере. Если пользователь переходит на другой экран, видео должно автоматически ставиться на паузу. Реализация в viewDidDisappear гарантирует, что пауза происходит после полного скрытия экрана — это предотвращает мерцание чёрного кадра при переходе.

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

Обнуление переменной player после паузы дополнительно освобождает память, занятую буферами видео. Этот подход особенно важен для приложений с длинными видеороликами, где буфер может занимать десятки мегабайт. Сочетание паузы с обнулением ссылок минимизирует footprint приложения в фоне.

viewDidDisappear и другие методы жизненного цикла

viewDidDisappear часто путают с viewWillDisappear и deinit, однако у каждого из этих методов своя зона ответственности. Понимание границ между ними — ключ к стабильной архитектуре iOS-приложения. Неверное использование может привести к двойному освобождению ресурсов или, наоборот, к их утечке.

Главное отличие viewDidDisappear от viewWillDisappear — момент вызова. viewWillDisappear вызывается, когда view ещё видна, но уже готовится к исчезновению. Это подходит для сохранения видимых данных (текст в полях ввода). viewDidDisappear вызывается после завершения анимации, когда view гарантированно не видна — идеально для освобождения ресурсов, не связанных с визуальным состоянием.

deinit, в отличие от viewDidDisappear, вызывается только при уничтожении объекта UIViewController в памяти. Если контроллер просто скрыт (например, покрыт модальным окном), deinit не вызывается. В этой ситуации viewDidDisappear — единственная точка для выполнения завершающих операций. Полное освобождение ресурсов должно происходить в deinit, но viewDidDisappear отвечает за временное освобождение до повторного появления.

Когда использовать какой метод

  • viewWillDisappear — сохранение вводимых данных, отправка аналитики о начале перехода
  • viewDidDisappear — остановка анимаций, отписка от уведомлений, скрытие overlay-элементов
  • deinit — финальное освобождение больших ресурсов, закрытие сетевых соединений

При разработке с использованием SwiftUI метод viewDidDisappear не применяется — его заменяет модификатор .onDisappear, который работает аналогичным образом. Однако в SwiftUI отсутствует прямой контроль над жизненным циклом, и разработчики полагаются на Combine и State-объекты для управления ресурсами. Для UIKit-приложений viewDidDisappear остаётся основным инструментом управления скрытием экрана.

Типичные ошибки при реализации

Даже опытные iOS-разработчики допускают ошибки в работе с viewDidDisappear. Рассмотрим пять наиболее распространённых проблем и способы их предотвращения. Знание этих anti-patterns поможет избежать трудноотлавливаемых багов, связанных с жизненным циклом контроллеров.

  • Пропуск super.viewDidDisappear — вызов super обязателен для корректной работы UIKit, его отсутствие может вызвать нарушение внутреннего состояния контроллера
  • Тяжёлые операции в viewDidDisappear — синхронная запись больших данных в viewDidDisappear блокирует главный поток и ухудшает анимацию перехода
  • Забытая отписка от уведомлений — если не вызвать removeObserver в viewDidDisappear, обработчик может сработать на zombie-объекте, вызвав EXC_BAD_ACCESS
  • Двойная отписка — удаление наблюдателя, который уже был удалён в другом месте, приводит к исключению NSInternalInconsistencyException
  • Зависимость от порядка вызова — во вложенных контейнерах порядок вызова viewDidDisappear у child и parent контроллеров не гарантирован

Особого внимания заслуживает потокобезопасность. Если viewDidDisappear вызывается на главном потоке (что гарантировано UIKit), но освобождение ресурсов включает асинхронные операции, необходимо синхронизировать доступ к разделяемым данным. Использование DispatchQueue.main.async внутри viewDidDisappear для обновления UI после завершения асинхронной задачи — распространённый, но корректный подход.

Ещё один важный anti-pattern — вызов методов делегата внутри viewDidDisappear, которые могут инициировать новый переход или модальный показ. Это создаёт цикл, в котором viewDidDisappear может быть вызван повторно до завершения первого вызова. Apple рекомендует избегать модальных показов внутри методов жизненного цикла, вынося их в отдельные обработчики событий.

Часто задаваемые вопросы

Чем viewDidDisappear отличается от viewWillDisappear?

viewWillDisappear вызывается перед началом анимации скрытия, когда view ещё видна. viewDidDisappear — после полного исчезновения view. Для сохранения данных используйте viewWillDisappear, для освобождения ресурсов — viewDidDisappear.

Нужно ли вызывать super.viewDidDisappear?

Да, вызов super.viewDidDisappear(animated) обязателен. UIKit использует этот метод для внутренних уведомлений и завершения состояния перехода. Без вызова super возможны сбои в UINavigationController и UITabBarController.

Может ли viewDidDisappear не вызваться?

Да, при интерактивном dismiss в iOS 13+ (свайп вниз) метод может не вызываться, если жест не завершён. Для гарантированного получения события используйте делегат UIAdaptivePresentationControllerDelegate и метод presentationControllerDidDismiss.

Что лучше: viewDidDisappear или deinit?

deinit вызывается только при уничтожении объекта, а viewDidDisappear при каждом скрытии. Для освобождения ресурсов при каждом переходе (например, отписка от уведомлений) используйте viewDidDisappear. Для финальной очистки при удалении контроллера — deinit.

Как работает viewDidDisappear в SwiftUI?

В SwiftUI вместо viewDidDisappear используется модификатор .onDisappear { }. Он вызывается при скрытии view из иерархии. В отличие от UIKit, SwiftUI не гарантирует вызов onDisappear во всех сценариях при анимациях.

Итоги

  • viewDidDisappear — последний метод жизненного цикла перед скрытием, вызывается после завершения анимации перехода
  • Основное назначение — освобождение ресурсов, остановка таймеров и отписка от уведомлений
  • Обязателен вызов super.viewDidDisappear для корректной работы UIKit
  • Отличается от viewWillDisappear моментом вызова: после анимации, а не до неё
  • Не заменяет deinit — deinit вызывается при уничтожении объекта, viewDidDisappear при каждом скрытии
  • Не используется для тяжёлых синхронных операций — они блокируют главный поток и нарушают анимацию
  • В iOS 13+ требуется дополнительная обработка через UIAdaptivePresentationControllerDelegate для гарантированного вызова

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также