Как обновить layout после изменения констрейнтов в iOS
Что проверяют
Понимаете ли вы цикл обновления в UIKit: изменение констрейнта не двигает вью мгновенно, а помечает раскладку как устаревшую. Следом почти всегда спрашивают про разницу между setNeedsLayout() и layoutIfNeeded() и про анимацию констрейнтов.
Как отвечать
Меняете constant, isActive или priority, и Auto Layout сам помечает иерархию к пересчёту: новые фреймы появятся в ближайшем проходе раскладки. Дальше вызов выбирается по задаче:
setNeedsLayout(): пометить вью к раскладке в следующем цикле. Дёшево и асинхронно, несколько вызовов подряд схлопываются в один проход;layoutIfNeeded(): выполнить отложенную раскладку прямо сейчас, синхронно. Нужен, когда фреймы требуются немедленно, и для анимации;setNeedsUpdateConstraints()иupdateConstraints(): пакетное обновление самих констрейнтов, если их пересчитывают централизованно. ВupdateConstraints()superвызывают последним шагом;viewDidLayoutSubviews()иlayoutSubviews(): место, где читать итоговые фреймы.
Анимация: вызвать layoutIfNeeded() у общего предка, чтобы сбросить накопленные изменения, поменять констрейнт, затем внутри UIView.animate(withDuration:) снова вызвать layoutIfNeeded() у того же предка. Для прерываемых анимаций подходит UIViewPropertyAnimator. Наборы констрейнтов переключают через NSLayoutConstraint.activate(_:) и deactivate(_:).
Конфликты ловите символьным брейкпоинтом UIViewAlertForUnsatisfiableConstraints, а не пролистыванием консоли.
Где спотыкаются
Вызывают layoutIfNeeded() у самой вью, чей констрейнт поменяли, а не у супервью. Анимация не проигрывается, элемент прыгает в конечное положение.
Держат констрейнт в weak аутлете и деактивируют его. Больше его никто не держит, он освобождается, и включить его снова уже не получится: ссылка стала nil.
Меняют приоритет установленного констрейнта с required (1000) на необязательный или обратно. Это не поддерживается и роняет приложение, поэтому переключаемому констрейнту сразу задают, например, 999.