В каком методе жизненного цикла UIViewController окончательно известны размеры его корневой вью (view)?

Ответ

Окончательные, актуальные размеры корневой вью (view.bounds) становятся известны в методе viewDidLayoutSubviews().

Почему именно здесь? Этот метод вызывается после того, как система:

  1. Рассчитала layout для всех subviews (включая корневую вью).
  2. Установила их окончательные frame (размеры и положение).

Особенности метода:

  • Многократный вызов: Вызывается при каждом изменении layout (поворот устройства, изменение размера, обновление constraints).
  • Используйте флаги для кода, который должен выполниться только при первом layout.

Пример получения размера вью:

private var isInitialLayoutDone = false

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    // Получаем окончательный размер корневой вью
    let viewSize = view.bounds.size
    print("View size: (viewSize)")

    // Код для однократного выполнения после первого layout
    if !isInitialLayoutDone {
        isInitialLayoutDone = true
        configureInitialLayoutBasedOnSize(viewSize)
    }
}

Важно: В viewDidLoad() и viewWillAppear(_:) размеры вью могут быть неверными (например, равными размеру экрана в Storyboard, а не реальному).

Ответ 18+ 🔞

А, слушай, вот эта вся история с размерами вью — это просто пиздец, конечно. Ну то есть, ты в viewDidLoad заходишь, такой весь уверенный, берешь view.bounds и думаешь: «О, сука, ну вот же он, мой размер!». А на деле это просто какая-то хуйня из сториборда, а не реальные цифры. Овердохуища разочарования.

Так вот, чтобы не обосраться с этим, есть один волшебный метод — viewDidLayoutSubviews(). Вот тут-то, блядь, уже всё по-честному. Система уже всё посчитала, расставила, все сабвьюхи на свои места загнала и только потом, как милостиво, говорит: «На, мудила, вот твои окончательные размеры, можешь пользоваться».

Но есть нюанс, ёпта! Этот метод вызывается не один раз, а как заведённый — на каждый чих. Повернул устройство? Вызвался. Констрейнты поменял? Вызвался. Ребёнок чихнул в соседней комнате? Возможно, тоже вызовется, я уже нихуя не уверен. Поэтому если тебе нужно что-то сделать один раз и только после того, как размеры стали настоящими, то без флага — никак. Иначе твой код будет выполняться, как последняя мартышлюшка, на каждый поворот экрана.

Смотри, как это выглядит в деле, чтобы не быть хитрожопым:

private var isInitialLayoutDone = false // Флаг, чтобы не дрочиться по сто раз

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    // Вот сейчас размер — это правда, блядь
    let viewSize = view.bounds.size
    print("View size: (viewSize)") // Ну наконец-то, реальные цифры, ебать

    // А это чтобы инициализацию свою ебанутую сделать только один раз
    if !isInitialLayoutDone {
        isInitialLayoutDone = true
        configureInitialLayoutBasedOnSize(viewSize) // Вот тут уже работаем с честными данными
    }
}

Короче, запомни: viewDidLoad и viewWillAppear — для них размеры вью это как кот, который сука собака, не поймёшь что. Всё настоящее происходит в viewDidLayoutSubviews. Главное — не наебнись с этим флагом, а то накрутишь логику так, что сам от себя охуеешь.