Ответ
Для позиционирования subviews на основе реальных размеров родительской view используйте viewDidLayoutSubviews().
Почему? В этом методе система уже применила все constraints, рассчитала frames с учётом safe area, размеров экрана и ориентации. Любые вычисления geometry здесь будут точными.
Пример настройки subview:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Безопасно используем bounds корневой view
let safeBounds = view.bounds.inset(by: view.safeAreaInsets)
headerView.frame = CGRect(
x: 0,
y: safeBounds.origin.y,
width: safeBounds.width,
height: 44
)
// Или ручная настройка autolayout constraints
customConstraint.constant = safeBounds.width * 0.8
}
Критические правила:
- Не вызывайте
layoutIfNeeded()внутриviewDidLayoutSubviews()— это может привести к рекурсивному циклу layout. - Метод вызывается многократно. Используйте флаги для одноразовой инициализации.
- Для динамической адаптивной вёрстки предпочтительнее использовать Auto Layout constraints. Ручной расчет frame в
viewDidLayoutSubviews— это "запасной" вариант для сложных или производительных сценариев.
Альтернатива для Auto Layout: Переопределите updateViewConstraints() для динамического обновления constraints в ответ на изменение размеров.
Ответ 18+ 🔞
Эй, слушай, вот смотри, как тут у нас с этими вьюхами на iOS. Ты ж знаешь, эту дичь, когда надо что-то поставить по размеру экрана, а оно плывёт, как говно в проруби.
Так вот, блядь, есть у нас такой метод, viewDidLayoutSubviews(). Вот в него и лезь, когда тебе надо наконец-то узнать, где у родительской вьюхи реальные границы, а не те, что она себе в viewDidLoad нафантазировала.
А че он такой особенный, этот метод? А потому что, ёпта, система к этому моменту уже всю свою магию с констрейнтами, сейф-эрией и ориентацией завершила. Фреймы посчитаны, всё расставлено. Тут уже можно брать линейку и чертить без страха, что через секунду всё съедет нахуй.
Смотри, как этим пользоваться, чтоб не обосраться:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Берём bounds, но отступаем от краёв, где эти чёртовы выемки и бамперы
let safeBounds = view.bounds.inset(by: view.safeAreaInsets)
// И вот теперь, сука, можно точно поставить хедер
headerView.frame = CGRect(
x: 0,
y: safeBounds.origin.y,
width: safeBounds.width,
height: 44
)
// Или, если ты констрейнтами балуешься, их тут же подкрутить
customConstraint.constant = safeBounds.width * 0.8
}
А теперь, блядь, главное, чтоб не вышло как всегда:
- Ни в коем случае, мать твою, не вызывай
layoutIfNeeded()внутри этого метода! Это как подлить бензину в костёр — пойдёт бесконечная рекурсия, и приложение просто накроется медным тазом. Система и так всё сама пересчитала. - Этот метод вызывается дохуя раз. Повернул телефон — вызвался. Скрыл клавиатуру — вызвался. Поэтому если тебе надо что-то сделать один раз (типа первоначальной расстановки), ставь флаг, а то заебёшься.
- Вообще-то, если ты не еблан, для адаптивки лучше использовать Auto Layout. Ручной расчёт фреймов в
viewDidLayoutSubviews— это как молотком гвозди забивать: мощно, но если промажешь, будет пиздец. Оставляй это для каких-то совсем уж ебнутых анимаций или супер-оптимизаций.
Ну а если ты всё же фанат констрейнтов?
Тогда тебе в updateViewConstraints(). Туда же, блядь, заходи, когда размеры поменялись, и подкручивай свои констрейнты динамически. Чтоб всё красиво и по фен-шую.