viewDidDisappear: суть методу, життєвий цикл UIViewController і коли викликається

Автор: IT Sectr Опубліковано: 2026-03-05 Час читання: 9 хв

viewDidDisappear — це метод життєвого циклу UIViewController, який викликається одразу після повного зникнення представлення з екрана пристрою iOS. Розробники використовують його для зупинки анімацій, звільнення оперативної пам'яті, відписки від сповіщень та збереження поточного стану. За даними Apple Developer Documentation (2025), коректна реалізація цього методу запобігає до 40% витоків пам'яті в застосунках з активною навігацією. Без нього фонові процеси можуть продовжувати роботу, споживаючи ресурси батареї та процесора. Правильне використання 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

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