Что происходит с корневым UIView при вызове viewDidLayoutSubviews
Что проверяют
Знаете ли вы, в какой момент размеры уже посчитаны, а в какой ещё нет. От этого зависит, где в контроллере читать bounds и где править то, что система ограничений не умеет, например слои.
Как отвечать
Первое уточнение: viewDidLayoutSubviews это метод UIViewController, а не UIView. У представления парный метод называется layoutSubviews.
Система зовёт его после того, как корневое представление контроллера разложило свои подпредставления. Порядок такой: viewWillLayoutSubviews, затем проход layoutSubviews у view и рекурсивно вниз по дереву, затем viewDidLayoutSubviews. К этому моменту view.frame и view.bounds актуальны, отступы безопасной зоны применены, фреймы подпредставлений посчитаны. Это и есть правильное место для всего, что зависит от размеров, но через ограничения не выражается:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
gradientLayer.frame = header.bounds
avatar.layer.cornerRadius = avatar.bounds.width / 2
scrollView.contentInset.bottom = keyboardInset
}Второе, что надо сказать обязательно: метод вызывается многократно, а не один раз за жизнь экрана. Его дёргает поворот, изменение bounds, появление клавиатуры, изменение содержимого, любой setNeedsLayout с последующим проходом. Поэтому внутри не создают подписки, не запускают анимации и не ходят в сеть.
Где спотыкаются
Путают метод контроллера с методом представления. Переопределять нужно ровно один из них: внутренняя раскладка своего компонента живёт в layoutSubviews, а согласование с экраном целиком в viewDidLayoutSubviews.
Второе: меняют внутри константы ограничений или фреймы так, что раскладка снова становится недействительной. Получается новый проход, он снова вызывает метод, и в худшем случае это бесконечный цикл с зависшим интерфейсом. Правка должна быть идемпотентной: сначала проверить, изменилось ли значение.
Третье: читают размеры раньше времени, в viewDidLoad или viewWillAppear, где bounds ещё равны значениям из файла разметки. Отсюда аватарки, круглые не на всех устройствах, и градиенты не на всю ширину.