> В каком методе жизненного цикла UIViewController известны размеры view (iOS, Swift)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: Совкомбанк, Яндекс

Стек: iOS, Swift

> Пример ответа

Короткий ответ

Размеры view гарантированно известны в viewDidLayoutSubviews() и в viewWillAppear(_:) - но только после первого вызова layout pass. В viewDidLoad() размеры ещё не финальны, так как view только загружена и не добавлена в иерархию с актуальными constraints. Надёжный способ - переопределить viewDidLayoutSubviews() или использовать view.safeAreaInsetsDidChange() для учёта safe area.

Подробное объяснение

Жизненный цикл UIViewController включает несколько этапов, и размеры view становятся известны не сразу:

  1. viewDidLoad() - view загружена из storyboard/xib или создана программно, но её frame ещё не установлен окончательно. Constraints могут быть не применены, а размер зависит от trait collection и ориентации, которые ещё не финализированы.

  2. viewWillAppear(_:) - вызывается перед добавлением view в window. К этому моменту layout обычно уже выполнен (если view была добавлена), но это не гарантировано для всех случаев, например при переходе с использованием UIViewControllerTransitionCoordinator.

  3. viewDidLayoutSubviews() - вызывается после того, как layout engine завершил расстановку subviews. Здесь view.bounds и view.frame уже соответствуют финальным значениям с учётом safe area, ориентации и trait changes. Это самый надёжный метод.

  4. viewDidAppear(_:) - view уже на экране, размеры точно известны, но это поздно для большинства задач, связанных с подготовкой UI.

Важно: viewDidLayoutSubviews() может вызываться multiple times - при изменении ориентации, размера, появлении клавиатуры или изменении safe area. Поэтому код внутри должен быть идемпотентным или защищён флагом.

На практике

Для senior-позиции важно не просто назвать метод, но и объяснить, почему viewDidLoad не подходит. Частая задача - настроить размеры элементов, зависящих от ширины экрана, или обновить layout после изменения safe area.

Практический паттерн: использовать viewDidLayoutSubviews() с проверкой на первое выполнение или на изменение размера:

SWIFT
private var didSetupInitialLayout = false
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard !didSetupInitialLayout else { return }
didSetupInitialLayout = true
// здесь размеры view.bounds уже корректны
configureLayout()
}

Также стоит упомянуть viewWillTransition(to:with:) для явной обработки изменений размера при повороте - но он не даёт актуальные размеры напрямую, только targetSize.

Пример кода

SWIFT
final class ProfileViewController: UIViewController {
private var isInitialLayoutDone = false
override func viewDidLoad() {
super.viewDidLoad()
// здесь нельзя полагаться на view.bounds - они могут быть .zero или нефинальными
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// размеры view.bounds корректны
let width = view.bounds.width
let height = view.bounds.height
// пример: настройка layout для collection view
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
let itemWidth = (width - 32) / 2
layout.itemSize = CGSize(width: itemWidth, height: itemWidth * 1.2)
}
// защита от повторной инициализации при каждом layout pass
if !isInitialLayoutDone {
isInitialLayoutDone = true
setupInitialConstraints()
}
}
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
// здесь можно реагировать на изменение safe area
updateInsets()
}
}

Как отвечать на собеседовании

Начните с прямого ответа: viewDidLayoutSubviews(). Затем добавьте контекст: объясните, почему viewDidLoad не подходит, упомяните, что viewWillAppear - ненадёжен для первого вызова, и что viewDidLayoutSubviews может вызываться многократно. Покажите понимание layout pass и safe area.

Хороший ответ для senior: "Размеры гарантированно известны в viewDidLayoutSubviews, но важно помнить, что этот метод вызывается не один раз. Для одноразовой инициализации используйте флаг. Также стоит учитывать viewSafeAreaInsetsDidChange для корректной работы с insets".

Что проверяет интервьюер

Интервьюер оценивает:

  • понимание жизненного цикла UIViewController, а не заученные методы;
  • знание, когда layout engine завершает работу;
  • умение работать с safe area и trait changes;
  • способность предвидеть проблемы (многократные вызовы, нулевые размеры в viewDidLoad);
  • практический опыт - как вы решали задачи с адаптивным layout.

Для senior-уровня важно показать, что вы понимаете разницу между view.frame и view.bounds, и почему в viewDidLayoutSubviews используется именно bounds.

Типичные ошибки

  • Утверждение, что размеры известны в viewDidLoad - это грубая ошибка, особенно для view, созданных программно.
  • Использование viewWillAppear для точных расчётов - на первом вызове layout может быть не выполнен.
  • Игнорирование многократных вызовов viewDidLayoutSubviews - приводит к повторной инициализации и лишней работе.
  • Неучёт safe area - размеры view.bounds включают всю область, включая под status bar и home indicator.
  • Попытка получить размеры в init или loadView - там view ещё не имеет корректного frame.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью