Где лучше выставлять констрейнты: в viewDidLoad или viewWillLayoutSubviews в iOS?
Что проверяют
Понимаете ли вы, какие методы вызываются один раз, а какие многократно. Ошибка здесь даёт накопление ограничений и падение с конфликтом.
Как отвечать
Создавать и активировать ограничения нужно в viewDidLoad. Этот метод вызывается один раз за жизнь контроллера, и именно там место разовой настройке иерархии.
viewWillLayoutSubviews и viewDidLayoutSubviews вызываются многократно: при повороте, изменении размера окна, появлении клавиатуры, обновлении разметки. Создавать ограничения там нельзя, они будут добавляться при каждом вызове, пока не появится конфликт или утечка. В этих методах уместно только то, что зависит от фактических размеров и не описывается ограничениями: кадры слоёв CALayer, скругления, градиенты, contentInset.
Как менять ограничения потом: не пересоздавать, а хранить ссылку и менять constant или переключать isActive.
override func viewDidLoad() {
super.viewDidLoad()
topConstraint = header.topAnchor.constraint(equalTo: view.topAnchor, constant: 16)
NSLayoutConstraint.activate([topConstraint, ...])
}
func expand() {
topConstraint.constant = 64
UIView.animate(withDuration: 0.25) { self.view.layoutIfNeeded() }
}Стоит упомянуть и updateConstraints: он существует для случаев, когда пересчёт ограничений действительно дорог и происходит часто. В обычном коде его переопределять не нужно, а если переопределили, добавлять там ограничения повторно тоже нельзя.
И обязательная деталь: при программной вёрстке у каждого добавленного вида ставится translatesAutoresizingMaskIntoConstraints = false, иначе система сама создаст ограничения из кадра и они вступят в конфликт с вашими.
Где спотыкаются
Ставят ограничения в viewWillLayoutSubviews и получают их дубликаты после первого же поворота.
Пересоздают ограничения вместо изменения constant.
Забывают отключить перевод авторазмеров и потом читают в консоли отчёт о конфликте с загадочными системными ограничениями.