Являются ли NSLayoutConstraints анимированными?
Я пытаюсь оживить некоторые представления, чтобы они были заблокированы гигантской клавиатурой в ландшафте. Он работает хорошо, если я просто анимации кадров, но другие предположили, что это контрпродуктивно, и я должен обновлять NSLayoutConstraints. Однако они не кажутся анимативными. Кто-нибудь получил их для работы с успехом?
//heightFromTop is an NSLayoutConstraint referenced from IB
[UIView animateWithDuration:0.25 animations:^{
self.heightFromTop.constant= 550.f;
}];
Результат - мгновенный переход к рассматриваемой высоте.
Ответы
Ответ 1
Просто следуйте этому точному шаблону:
self.heightFromTop.constant = 550.0f;
[myView setNeedsUpdateConstraints];
[UIView animateWithDuration:0.25f animations:^{
[myView layoutIfNeeded];
}];
где myView
- это вид, в который был добавлен self.heightFromTop
. Ваше представление "прыгает", потому что единственное, что вы делали в блоке анимации, - это установить ограничение, которое не вызывает макеты сразу. В вашем коде макет происходит в следующем цикле запуска после установки heightFromTop.constant
, и к тому времени вы уже выходите за рамки блока анимации.
В Swift 2:
self.heightFromTop.constant = 550
myView.setNeedsUpdateConstraints()
UIView.animateWithDuration(0.25, animations: {
myView.layoutIfNeeded()
})
Ответ 2
Предлагаемый Apple способ немного отличается (См. пример в разделе "Анимация изменений, сделанных с помощью автоматической компоновки" ). Сначала вам нужно вызвать layoutIfNeeded перед анимацией. Затем добавьте свои анимационные материалы внутри блока анимации, а затем снова вызовите layoutIfNeeded. Для таких парней, как я, которые переходят на автозапуск, это более похоже на предыдущие анимации, которые мы делали с кадрами внутри блоков анимации. Нам просто нужно вызвать layoutIfNeeded дважды - перед анимацией и после анимации:
[self.view layoutIfNeeded]; // Ensures that all pending layout operations have been completed
[UIView animateWithDuration:1.0f animations:^{
// Make all constraint changes here
self.heightFromTop.constant= 550.f;
[self.view layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes
}];
Ответ 3
Я попробовал подход @Centurion, но каким-то образом мой взгляд оживил бы неправильный фрейм, если он будет загружен из раскадровки. Проблема исчезнет, если я заменил первый layoutIfNeeded
на updateConstraintsIfNeeded
, хотя я понятия не имею, почему. Если кто-нибудь может дать объяснение, это будет очень полезно.
[self.view updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0 animations:^{
self.myConstraint.constant= 100;
[self.view layoutIfNeeded];
}];
Ответ 4
У меня была аналогичная проблема, и этот поток был большой помощью, чтобы пройти мимо него.
Ответ от erurainon поставил меня на правильный путь, но я хотел бы предложить немного другой ответ. Предлагаемый код от erurainon не работал у меня, поскольку у меня все же был прыжок, а не анимированный переход. Ссылка, предоставленная cnotethegr8, дала мне рабочий ответ:
Руководство по автоматическому компоновке
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html (вплоть до нижней части страницы).
Несколько отличий от ответа erurainon:
- Вызов layoutIfNeeded в представлении контейнера перед вызовом метода анимации (а вместо setNeedsUpdateConstraints в myView).
- Задайте новое ограничение в блоке анимации.
- Вызов layoutIfNeeded в представлении контейнера в методе анимации (после установки ограничения), а не в myView.
Это будет соответствовать шаблону, предложенному Apple в приведенной выше ссылке.
Пример
Я хотел анимировать определенный вид, закрывая или расширяя его одним нажатием кнопки. Поскольку я использую автозапуск и не хочу жестко кодировать любые размеры (в моем случае высоты) в коде, я решил захватить высоту в viewDidLayoutSubviews. Вам нужно использовать этот метод, а не viewWillAppear при использовании автозапуска. Поскольку viewDidLayoutSubviews можно вызвать много раз, я использовал BOOL, чтобы сообщить мне о первом выполнении для моей инициализации.
// Code snippets
@property (weak, nonatomic) IBOutlet UIView *topView; // Container for minimalView
@property (weak, nonatomic) IBOutlet UIView *minimalView; // View to animate
@property (nonatomic) CGFloat minimalViewFullHeight; // Original height of minimalView
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *minimalViewHeightConstraint;
@property (nonatomic) BOOL executedViewDidLayoutSubviews;
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
// First execution of viewDidLayoutSubviews?
if(!self.executedViewDidLayoutSubviews){
self.executedViewDidLayoutSubviews = YES;
// Record some original dimensions
self.minimalViewFullHeight = self.minimalView.bounds.size.height;
// Setup our initial view configuration & let system know that
// constraints need to be updated.
self.minimalViewHeightConstraint.constant = 0.0;
[self.minimalView setNeedsUpdateConstraints];
[self.topView layoutIfNeeded];
}
}
Обрезать полный фрагмент действия
// An action to close our minimal view and show our normal (full) view
- (IBAction)resizeFullAction:(UIButton *)sender {
[self.topView layoutIfNeeded];
[UIView transitionWithView:self.minimalView
duration:1.0
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
self.minimalViewHeightConstraint.constant = 0.0;
// Following call to setNeedsUpdateConstraints may not be necessary
[self.minimalView setNeedsUpdateConstraints];
[self.topView layoutIfNeeded];
} completion:^(BOOL finished) {
;
}];
// Other code to show full view
// ...
}
Уменьшить фрагмент небольшого действия
// An action to open our minimal view and hide our normal (full) view
- (IBAction)resizeSmallAction:(UIButton *)sender {
[self.topView layoutIfNeeded];
[UIView transitionWithView:self.minimalView
duration:1.0
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
self.minimalViewHeightConstraint.constant = self.minimalViewFullHeight;
[self.minimalView setNeedsUpdateConstraints];
[self.topView layoutIfNeeded];
} completion:^(BOOL finished) {
;
}];
// Other code to hide full view
// ...
}
Вы можете использовать animateWithDuration вместо transitionWithView, если хотите.
Надеюсь, что это поможет.