Ответ
Основной метод — viewDidLayoutSubviews().
Почему он предпочтительнее viewWillLayoutSubviews?
- В
viewDidLayoutSubviews()гарантировано, что все вычисления Auto Layout завершены и все сабвью имеют свои окончательные (final) размеры (bounds,frame). viewWillLayoutSubviews()вызывается перед вычислением лэйаута. Размеры вью и её сабвью здесь могут быть неактуальными или промежуточными.
Пример позиционирования:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Безопасно используем окончательные размеры
let side = min(view.bounds.width, view.bounds.height) * 0.8
centeredView.frame = CGRect(
x: (view.bounds.width - side) / 2,
y: (view.bounds.height - side) / 2,
width: side,
height: side
)
}
Альтернативы для кастомных UIView:
layoutSubviews()— аналог дляUIView. Здесь также можно позиционировать сабвью, опираясь на окончательныйbounds.- Использование Auto Layout констрейнтов — наиболее декларативный и правильный способ, который не требует ручного вызова этих методов.
Ответ 18+ 🔞
Давай разжую, как это работает, а то некоторые до сих пор пытаются в viewWillLayoutSubviews размеры ловить, а потом удивляются, почему у них всё пляшет, как дервиш на раскалённой сковороде.
Смотри, есть два главных метода, где можно с лэйаутом работать: viewWillLayoutSubviews и viewDidLayoutSubviews. Разница между ними — как между «сейчас начнём готовить» и «блюдо уже на столе, можно жрать».
viewWillLayoutSubviews() — это как шеф-повар только зашёл на кухню и крикнул: «Так, щас будем готовить!». Инструменты на месте? Не факт. Продукты нарезаны? Хуй там. Автолэйаут только собирается свои вычисления делать, все размеры (bounds, frame) — это прошлогодний снег, на который опираться нельзя. Если ты тут начнёшь свою вьюху позиционировать, она встанет, а потом система придет и скажет: «А теперь по-настоящему!» — и всё съедет. Короче, тут делать нечего, кроме как подготовить какие-нибудь начальные данные, если очень надо.
А вот viewDidLayoutSubviews() — это уже готовый обед, можно садиться и есть. В этот момент система уже всё посчитала, все констрейнты отработали, и каждый сабвью получил свои финальные, блядь, размеры. Твоя вьюха знает, сколько места у неё есть, и ты можешь безопасно, с чистой совестью, расставлять в ней что угодно, опираясь на view.bounds.
Вот, смотри, как это выглядит на практике. Допустим, тебе надо в центр экрана впихнуть квадрат, который занимает 80% от меньшей стороны:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Теперь view.bounds — это святая истина, а не гадание на кофейной гуще
let side = min(view.bounds.width, view.bounds.height) * 0.8
centeredView.frame = CGRect(
x: (view.bounds.width - side) / 2,
y: (view.bounds.height - side) / 2,
width: side,
height: side
)
}
Всё. Красиво, предсказуемо, и не будет дергаться при каждом повороте экрана.
А что насчёт кастомных UIView?
Там своя кухня. Аналог viewDidLayoutSubviews для обычной вьюхи — это метод layoutSubviews(). Туда же приходит сигнал, что твоя вьюха получила свои окончательные размеры, и можно уже внутри неё сабвьюшки по bounds расставлять.
Но слушай сюда, самый правильный путь, чтобы не ебаться с этими ручными рамками — это Auto Layout констрейнты. Это когда ты не пишешь frame = ..., а говоришь системе: «Хочу, чтобы эта кнопка была по центру, а эта картинка — на 20 пунктов ниже». Система сама всё посчитает в viewDidLayoutSubviews и расставит как надо. Это декларативно, надёжно и не требует вызова супер-методов вручную. Но если уж взялся за ручное управление, то помни — твой домен это viewDidLayoutSubviews, а не эта предварительная болтовня в viewWillLayoutSubviews.