Жизненный цикл ViewController
Порядок вызовов
init(coder:)/init(nibName:bundle:)/ свойinit— объект создан, view ещё нет.loadView()— вызывается лениво, при первом обращении кview(не при создании контроллера). Загружает view из storyboard/XIB или создаёт пустую; переопределяют, чтобы собрать корневую view в коде, и тогдаsuper.loadView()не вызывают.viewDidLoad()— view в памяти, outlets подключены. Вызывается один раз. Здесь добавляют subviews, констрейнты, подписки.boundsуже есть, но размер не финальный (из storyboard или нулевой) — геометрию тут не читают.viewWillAppear(_:)— view сейчас добавят в иерархию. Вызывается при каждом показе. Подходит для обновления данных, которые могли измениться, пока экран был скрыт.viewIsAppearing(_:)(iOS 17, back-deploy до iOS 13) — view уже в иерархии,traitCollectionи геометрия родителя актуальны, но первый кадр ещё не нарисован. Apple рекомендует именно его для UI, зависящего от размеров и traits (вместоviewWillAppearиviewDidLayoutSubviews).viewWillLayoutSubviews()/viewDidLayoutSubviews()— вокругlayoutSubviews()корневой view; вызываются много раз (поворот, клавиатура, изменение констрейнтов). Здесь — ручной layout и чтение финальныхframe.viewDidAppear(_:)— экран показан, анимация перехода завершена. Старт анимаций, запрос разрешений, фокус на поле. Начинать загрузку данных тут поздно — экран успеет показаться пустым; её запускают вviewDidLoad.viewWillDisappear(_:)— view уходит с экрана: pop, dismiss, push следующего контроллера, переключение вкладки. Сохранение черновика, остановка таймеров.viewDidDisappear(_:)— view убрана из иерархии.deinit— контроллер освобождён. Если не вызывается после закрытия — утечка (чаще всего retain cycle в замыкании).
Продолжение ответа с кодом и уточняющими вопросами интервьюера — в приложении DevPrep.
Полный ответ — в DevPrep