Анимация изменения констрейнта (constraint) в Swift: как сделать плавно?
Правильная анимация констрейнтов в Swift
Проблема в том, что анимация для констрейнтов работает иначе, чем для стандартных свойств (frame, alpha и т.д.). Когда вы меняете константу констрейнта, система просто обновляет значение, но не запускает анимацию автоматически. Чтобы анимация сработала, нужно явно попросить layout-систему перерисовать вью внутри блока анимации.
Вот правильный код:
@IBOutlet weak var myViewHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var myView: UIView!
@IBAction func toggleView(sender: UIButton) {
// 1. Меняем константу констрейнта
if myViewHeightConstraint.constant == 100 {
myViewHeightConstraint.constant = 200
} else {
myViewHeightConstraint.constant = 100
}
// 2. Запускаем анимацию с вызовом layoutIfNeeded()
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseInOut, animations: {
self.myView.layoutIfNeeded() // ИЛИ self.view.layoutIfNeeded()
}, completion: nil)
}
Почему это работает?
- layoutIfNeeded() — принудительно запускает перерисовку (layout) для вью и его сабвью. Внутри блока анимации UIKit анимирует любые изменения frame, которые происходят в результате пересчета констрейнтов.
- Вы можете вызывать
layoutIfNeeded()на самой вью (myView), на её супервью, или на корневом view контроллера — в зависимости от того, какие констрейнты нужно анимировать. Обычно достаточно вызвать на родительском view. - Важно: не ставьте
layoutIfNeeded()до изменения константы — это сбросит все изменения.
Дополнительные советы
- Анимация нескольких констрейнтов: просто измените константы всех нужных констрейнтов, а затем вызовите
layoutIfNeeded()один раз внутри блока анимации. - Параметры анимации: используйте
.curveEaseInOut,.curveEaseInили.spring(с iOS 7+). Для пружинной анимации:UIView.animate(withDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:). - Проверьте, что констрейнты не конфликтуют: если у вью есть другие констрейнты (например, привязанные к верхнему краю), анимация может не сработать или сработать некорректно. Используйте Debug View Hierarchy в Xcode для проверки.
Пример с пружинной анимацией
UIView.animate(
withDuration: 0.5,
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.5,
options: .curveEaseInOut,
animations: {
self.myView.layoutIfNeeded()
},
completion: nil
)
Этот подход работает во всех версиях iOS, начиная с iOS 6, и является стандартным для анимации констрейнтов.
<<Альтернативный подход: анимация через transform (для простых случаев)
Если вам нужно анимировать только размер или положение одной вью, и констрейнты не критичны (например, вью без сложной верстки), можно использовать CGAffineTransform. Это проще и не требует вызова layoutIfNeeded():
@IBAction func toggleView(sender: UIButton) {
let scale: CGFloat = (myView.transform == .identity) ? 2.0 : 1.0
UIView.animate(withDuration: 0.3) {
self.myView.transform = CGAffineTransform(scaleX: scale, y: scale)
}
}
Но у этого метода есть недостатки:
- Transform не влияет на layout других вью — они не будут подстраиваться под новый размер.
- При использовании с Auto Layout могут возникнуть проблемы с перерисовкой (например, у текста внутри).
- Лучше всего подходит для анимаций, которые не должны ломать сетку констрейнтов (например, эффект "нажатия" или временное увеличение).
Поэтому для большинства случаев рекомендуется первый способ с layoutIfNeeded().
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги