Как мне анимировать изменения ограничений?

Я обновляю старое приложение с AdBannerView, и когда нет рекламы, оно ускользает с экрана. Когда есть реклама, она скользит по экрану. Основные вещи.

Старый стиль, я установил кадр в блок анимации. Новый стиль, у меня есть IBOutlet к ограничению автоматической компоновки, которое определяет позицию Y, в данном случае это расстояние от нижней части супервизора, и изменяю константу:

- (void)moveBannerOffScreen {
    [UIView animateWithDuration:5 animations:^{
        _addBannerDistanceFromBottomConstraint.constant = -32;
    }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen {
    [UIView animateWithDuration:5 animations:^{
        _addBannerDistanceFromBottomConstraint.constant = 0;
    }];
    bannerIsVisible = TRUE;
}

И баннер перемещается, как и ожидалось, но без анимации.


ОБНОВЛЕНИЕ. Я повторно посмотрел WWDC 12 talk Best Practices for Mastering Автоматический макет, который охватывает анимацию. В нем обсуждается, как обновить ограничения с помощью CoreAnimation:

Я пробовал использовать следующий код, но получил те же результаты:

- (void)moveBannerOffScreen {
    _addBannerDistanceFromBottomConstraint.constant = -32;
    [UIView animateWithDuration:2 animations:^{
        [self.view setNeedsLayout];
    }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen {
    _addBannerDistanceFromBottomConstraint.constant = 0;
    [UIView animateWithDuration:2 animations:^{
        [self.view setNeedsLayout];
    }];
    bannerIsVisible = TRUE;
}

Кстати, я много раз проверял, и это выполняется в основном потоке.


person DBD    schedule 27.09.2012    source источник
comment
Я никогда раньше не видел так много голосов за вопрос и ответ на опечатку в SO.   -  person abbood    schedule 01.10.2013
comment
Если в ответе есть опечатка, отредактируйте ответ. Вот почему они доступны для редактирования.   -  person i_am_jorf    schedule 07.10.2014
comment
@jeffamaphone - Было бы полезнее, если бы вы указали на опечатку, чтобы я знал, в чем ошибка. Вы можете сами отредактировать ответ и исправить опечатку, сохранив всем остальным нашу обличительную речь. Я просто отредактировал его, чтобы удалить константу из блока анимации, если это то, о чем вы имели в виду.   -  person DBD    schedule 07.10.2014
comment
Я не знаю, что это за опечатка. Я отвечал на комментарии выше.   -  person i_am_jorf    schedule 07.10.2014
comment
Тогда опечатка - это вопрос. По глупости я набирал setNeedsLayout вместо layoutIfNeeded. Это ясно показано в моем вопросе, когда я вырезал и вставлял свой код с ошибкой и скриншоты с правильной командой. Но я не мог этого заметить, пока кто-то не указал на это.   -  person DBD    schedule 07.10.2014


Ответы (12)


Два важных примечания:

  1. Вам нужно вызвать layoutIfNeeded в блоке анимации. Apple на самом деле рекомендует вам вызвать его один раз перед блоком анимации, чтобы убедиться, что все ожидающие операции макета были завершены.

  2. Вам нужно вызвать его специально в родительском представлении (например, self.view), а не в дочернем представлении, к которому привязаны ограничения. Это приведет к обновлению всех ограниченных представлений, включая анимацию других представлений, которые могут быть ограничены представлением, для которого вы изменили ограничение (например, представление B прикреплено к нижней части представления A, а вы только что изменили представление A верхнее смещение, и вы хотите, чтобы View B анимировался вместе с ним)

Попробуй это:

Цель-C

- (void)moveBannerOffScreen {
    [self.view layoutIfNeeded];

    [UIView animateWithDuration:5
        animations:^{
            self._addBannerDistanceFromBottomConstraint.constant = -32;
            [self.view layoutIfNeeded]; // Called on parent view
        }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen { 
    [self.view layoutIfNeeded];

    [UIView animateWithDuration:5
        animations:^{
            self._addBannerDistanceFromBottomConstraint.constant = 0;
            [self.view layoutIfNeeded]; // Called on parent view
        }];
    bannerIsVisible = TRUE;
}

Swift 3

UIView.animate(withDuration: 5) {
    self._addBannerDistanceFromBottomConstraint.constant = 0
    self.view.layoutIfNeeded()
}
person g3rv4    schedule 30.09.2012
comment
Вы пробовали изменить константу и layoutIfNeeded внутри блока анимации, верно? - person g3rv4; 03.10.2012
comment
да, я пробовал это. У меня так было изначально, и я изменил его только после второго просмотра видео WWDC. - person DBD; 03.10.2012
comment
Других изменений ограничений нет. Ограничения для рекламного баннера: расстояние от левого края (0), расстояние от правого края (0), фиксированная высота (32) и расстояние от низа (0). Никаких других ограничений, связанных с баннером, нет. Комментирование изменения ограничения будет означать, что ничего не происходит. - person DBD; 05.10.2012
comment
Вы знаете, что ... ваш ответ работает. WWDC работает .... мое видение не работает. По какой-то причине мне потребовалась неделя, чтобы понять, что я звоню setNeedsLayout вместо layoutIfNeeded. Я немного в ужасе от того, сколько часов я провел, не замечая, что просто набрал неправильное имя метода. - person DBD; 15.10.2012
comment
Решение работает, но вам не нужно изменять константу ограничения внутри блока анимации. Совершенно нормально установить ограничение один раз перед запуском анимации. Вам следует отредактировать свой ответ. - person Ortwin Gentz; 14.01.2013
comment
Сначала это не сработало для меня, а затем я понял, что вам нужно вызывать layoutIfNeeded в представлении PARENT, а не в представлении, к которому применяются ограничения. - person Oliver Pearmain; 21.03.2013
comment
использование layoutIfNeeded будет анимировать все обновления вложенного представления, а не только изменение ограничения. как сделать анимацию только изменения ограничения? - person ngb; 24.08.2013
comment
отлично работает - спасибо. но для того, чтобы нам было ясно: это чистое безумие, когда ограничения, по сути, существуют в совершенно другой области для анимации. это действительно вызывает некоторую очень чистую старую логику анимации. - person natbro; 17.01.2015
comment
Я не мог заставить это работать, думая, что мне нужно вызвать layoutIfNeeded в родительском представлении ограничения. Я изменил его на родительский вид контроллера представления, и теперь он прекрасно работает - person Jesse; 07.04.2015
comment
Также будьте осторожны с UILabels. Некоторая анимация типов с UILabel работает по-разному с тем же кодом с простым UIView. К сожалению, не знаю почему. И если кто-то знает об этом, мне очень приятно прокомментировать меня. - person Victor; 27.06.2015
comment
Это работает отлично, и я хотел бы отметить, что я вызываю layoutIfNeeded в подпредставлении, а не в родительском представлении. Не совсем уверен, почему другие должны называть это родителем. - person user3344977; 17.07.2015
comment
Apple на самом деле рекомендует вам вызвать его один раз перед блоком анимации, чтобы убедиться, что все ожидающие операции макета были завершены, спасибо, никогда об этом не думал, но это имеет смысл. - person Rick van der Linde; 22.11.2015
comment
Мне это понравилось, но мне нужен эффект EaseIn, и я обнаружил, что это прекрасно работает в Swift: UIView.animateWithDuration (0.3, delay: 0, options: UIViewAnimationOptions.CurveEaseIn, анимация: {self.view.layoutIfNeeded ()}, завершение: nil) - person James Toomey; 12.06.2016
comment
Это не работает со связанными ограничениями. Допустим, мы анимируем высоту A с этим, высота B, которая зависит от высоты A, будет короче, если высота A длинная, и длиннее, если высота A короткая. B, к сожалению, сразу изменится и НЕ будет анимировать. - person Jonny; 23.08.2016
comment
Иногда анимация не анимируется и не запускается сразу до завершения. Как это решить? - person He Yifei 何一非; 02.09.2016
comment
Я несколько раз успешно реализовал ваше решение, сегодня оно не подойдет для новой реализации. Выяснилось, что вам нужно вызвать self.view.layoutIfNeeded() в блоке анимации, а НЕ в блоке завершения. Дох! - person Rool Paap; 05.09.2016
comment
Apple действительно рекомендует, что ссылка не работает. Если кто-то знает новый URL, могут ли они обновить его? - person Liron Yahdav; 04.10.2016
comment
Пример кода выглядел бы лучше, если бы в обратном вызове вызывали [subview.superview layoutIfNeeded]; - person art-divin; 24.01.2017
comment
@ngb Установите все ограничения, которые вы не хотите анимировать - ›вызовите layoutIfNeeded, а затем настройте блок анимации с тем одним ограничением, которое вы хотите анимировать, и другим layoutIfNeeded включенным. - person Viktor Kucera; 12.06.2017
comment
Да, он работает на iOS 11 beta5, и не нужно обновлять ограничение внутри блока анимации. - person Chauyan; 22.08.2017
comment
Сначала у меня это не сработало, но потом я понял, что вместо этого нужно поместить isHidden = false из блока анимации в блок завершения. - person Eugenio; 15.11.2017
comment
Я запутался в способе обновления ограничений и написал это. Не могли бы вы взглянуть? - person Honey; 15.12.2017
comment
это не правильно. Ограничения должны обновляться вне блока анимации, а внутри блока анимации должен быть только view.layoutIfNeeded () - person Paul T.; 30.10.2020

Я ценю предоставленный ответ, но я думаю, что было бы неплохо пойти немного дальше.

Базовая блочная анимация из документации

[containerView layoutIfNeeded]; // Ensures that all pending layout operations have been completed
[UIView animateWithDuration:1.0 animations:^{
     // Make all constraint changes here
     [containerView layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes
}];

но на самом деле это очень упрощенный сценарий. Что, если я хочу анимировать ограничения подпредставления с помощью метода updateConstraints?

Блок анимации, который вызывает метод updateConstraints подпредставлений

[self.view layoutIfNeeded];
[self.subView setNeedsUpdateConstraints];
[self.subView updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionLayoutSubviews animations:^{
    [self.view layoutIfNeeded];
} completion:nil];

Метод updateConstraints переопределяется в подклассе UIView и должен вызывать super в конце метода.

- (void)updateConstraints
{
    // Update some constraints

    [super updateConstraints];
}

AutoLayout Guide оставляет желать лучшего, но оно того стоит чтение. Я сам использую это как часть UISwitch, которая переключает подпредставление с парой UITextField с простой и тонкой анимацией коллапса (длительностью 0,2 секунды). Ограничения для подпредставления обрабатываются в методах updateConstraints подклассов UIView, как описано выше.

person Cameron Lowell Palmer    schedule 22.01.2014
comment
При вызове всех вышеперечисленных методов в self.view (а не в подпредставлении этого представления) вызов updateConstraintsIfNeeded не требуется (поскольку setNeedsLayout также запускает updateConstraints этого представления). Для большинства это может быть тривиально, но до сих пор это было не для меня;) - person anneblue; 24.02.2014
comment
Трудно комментировать, не зная полного взаимодействия Autolayout и компоновки пружин и распорок в вашем конкретном случае. Я говорю исключительно о сценарии чистого автопроката. Мне было бы любопытно узнать, что именно происходит в вашем методе layoutSubviews. - person Cameron Lowell Palmer; 24.02.2014
comment
translatesAutoresizingMaskIntoConstraints = NO; Если вы хотите использовать чистую автоматическую раскладку, вам обязательно нужно отключить это. - person Cameron Lowell Palmer; 25.02.2014
comment
Я не видел этого, но я не могу говорить об этой проблеме без кода. Может быть, вы могли бы создать макет TableView и разместить его на GitHub? - person Cameron Lowell Palmer; 26.02.2014
comment
Извините, я не использую gitHub :( - person anneblue; 26.02.2014
comment
Осторожно: при использовании UIViewAnimationOptionBeginFromCurrentState ограничения макета будут установлены ДО анимации! - person Robert; 12.09.2014
comment
Я запутался в способе обновления ограничений и написал это. Не могли бы вы взглянуть? - person Honey; 15.12.2017

Как правило, вам просто нужно обновить ограничения и вызвать layoutIfNeeded внутри блока анимации. Это может быть изменение свойства .constant объекта NSLayoutConstraint, добавление ограничений удаления (iOS 7) или изменение свойства ограничений .active (iOS 8 и 9).

Образец кода:

[UIView animateWithDuration:0.3 animations:^{
    // Move to right
    self.leadingConstraint.active = false;
    self.trailingConstraint.active = true;

    // Move to bottom
    self.topConstraint.active = false;
    self.bottomConstraint.active = true;

    // Make the animation happen
    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];
}];

Пример настройки:

«Проект

Полемика

Возникает ряд вопросов о том, следует ли изменять ограничение перед блоком анимации или внутри его (см. Предыдущий ответы).

Ниже приведен разговор в Twitter между Мартином Пилкингтоном, преподающим iOS, и Кеном Ферри, написавшим Auto Layout. Кен объясняет, что, хотя изменение констант вне блока анимации в настоящее время может работать, это небезопасно, и на самом деле они должны быть изменены внутри блока анимации. https://twitter.com/kongtomorrow/status/440627401018466305

Анимация:

Образец проекта

Вот простой проект, показывающий, как можно анимировать представление. Он использует цель C и анимирует представление, изменяя свойство .active нескольких ограничений. https://github.com/shepting/SampleAutoLayoutAnimation

person Steven Hepting    schedule 13.11.2015
comment
+1 за показ примера использования нового активного флага. Кроме того, изменение ограничения за пределами блока анимации всегда казалось мне взломом. - person Korey Hinton; 25.11.2015
comment
Я поднял проблему в github, потому что это решение не работает для перемещения представления туда, где оно было. Я считаю, что изменение активности - неправильное решение, и вместо этого вам следует изменить приоритет. Установите верхнее значение 750, а нижнее значение 250, затем в коде чередуйте UILayoutPriorityDefaultHigh и UILayoutPriorityDefaultLow. - person malhal; 11.01.2016
comment
Другая причина для обновления внутри блока заключается в том, что он изолирует изменения от вызывающего кода, который может также вызвать layoutIfNeeded. (и спасибо за ссылку в Твиттере) - person Chris Conover; 23.06.2016
comment
Отличный ответ. Тем более, что вы упомянули разговор Мартина и Кена об этом. - person Jona; 18.11.2017
comment
Я запутался в способе обновления ограничений и написал это. Не могли бы вы взглянуть? - person Honey; 15.12.2017
comment
это не правильно. Ограничения должны обновляться вне блока анимации, а внутри блока анимации должен быть только view.layoutIfNeeded () - person Paul T.; 30.10.2020

Решение Swift 4

UIView.animate

Три простых шага:

  1. Измените ограничения, например:

    heightAnchor.constant = 50
    
  2. Сообщите содержащему view, что его макет грязный и что автоматическая раскладка должна пересчитать макет:

    self.view.setNeedsLayout()
    
  3. В блоке анимации скажите макету, чтобы он пересчитал макет, что эквивалентно установке кадров напрямую (в этом случае автоматическая раскладка установит кадры):

    UIView.animate(withDuration: 0.5) {
        self.view.layoutIfNeeded()
    }
    

Полный простейший пример:

heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
    self.view.layoutIfNeeded()
}

Примечания

Существует необязательный 0-й шаг - перед изменением ограничений вы можете вызвать self.view.layoutIfNeeded(), чтобы убедиться, что начальная точка для анимации находится в состоянии с примененными старыми ограничениями (в случае, если были некоторые другие изменения ограничений, которые не должны быть включены в анимация):

otherConstraint.constant = 30
// this will make sure that otherConstraint won't be animated but will take effect immediately
self.view.layoutIfNeeded()

heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
    self.view.layoutIfNeeded()
}

UIViewPropertyAnimator

Поскольку с iOS 10 у нас появился новый механизм анимации - UIViewPropertyAnimator, мы должны знать, что в основном к нему применяется тот же механизм. Шаги в основном такие же:

heightAnchor.constant = 50
self.view.setNeedsLayout()
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
animator.addAnimations {
    self.view.layoutIfNeeded()
}
animator.startAnimation()

Поскольку animator - это инкапсуляция анимации, мы можем сохранить ссылку на нее и вызвать ее позже. Однако, поскольку в блоке анимации мы просто указываем автопересчету кадров, мы должны изменить ограничения перед вызовом startAnimation. Поэтому возможно что-то вроде этого:

// prepare the animator first and keep a reference to it
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
animator.addAnimations {
    self.view.layoutIfNeeded()
}

// at some other point in time we change the constraints and call the animator
heightAnchor.constant = 50
self.view.setNeedsLayout()
animator.startAnimation()

Порядок изменения ограничений и запуска аниматора важен - если мы просто изменим ограничения и оставим наш аниматор на более поздний момент, следующий цикл перерисовки может вызвать пересчет автоматического раскладки, и изменение не будет анимировано.

Кроме того, помните, что один аниматор нельзя использовать повторно - после того, как вы запустите его, вы не сможете «запустить» его повторно. Так что я полагаю, что на самом деле нет веских причин держать аниматора при себе, если только мы не используем его для управления интерактивной анимацией.

person Milan Nosáľ    schedule 16.02.2018
comment
layoutIfNeeded () - это ключ - person Medhi; 03.04.2020

Раскадровка, код, советы и несколько замечаний

Другие ответы хороши, но этот подчеркивает несколько довольно важных подводных камней анимации ограничений на недавнем примере. Я прошел через множество вариантов, прежде чем понял следующее:

Сделайте ограничения, на которые вы хотите нацелить, в переменные класса, чтобы они содержали сильную ссылку. В Swift я использовал ленивые переменные:

lazy var centerYInflection:NSLayoutConstraint = {
       let temp =  self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first
        return temp!
}()

После некоторых экспериментов я заметил, что ДОЛЖНО получить ограничение из представления ВЫШЕ (также известного как супервизор) двух представлений, в которых определено ограничение. В приведенном ниже примере (и MNGStarRating, и UIWebView - это два типа элементов, между которыми я создаю ограничение, и они являются подпредставлениями внутри self.view).

Сцепление фильтров

Я использую метод фильтрации Swift, чтобы отделить желаемое ограничение, которое будет точкой перегиба. Можно было бы также сделать намного более сложным, но фильтр здесь неплохо справляется.

Анимация ограничений с помощью Swift

Nota Bene - этот пример представляет собой решение для раскадровки / кода и предполагает, что в раскадровке установлены ограничения по умолчанию. Затем можно анимировать изменения с помощью кода.

Предполагая, что вы создаете свойство для фильтрации с точными критериями и переходите к определенной точке перегиба для вашей анимации (конечно, вы также можете фильтровать массив и выполнять цикл, если вам нужно несколько ограничений):

lazy var centerYInflection:NSLayoutConstraint = {
    let temp =  self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first
    return temp!
}()

....

Некоторое время спустя...

@IBAction func toggleRatingView (sender:AnyObject){

    let aPointAboveScene = -(max(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height) * 2.0)

    self.view.layoutIfNeeded()


    //Use any animation you want, I like the bounce in springVelocity...
    UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.75, options: [.CurveEaseOut], animations: { () -> Void in

        //I use the frames to determine if the view is on-screen
        if CGRectContainsRect(self.view.frame, self.ratingView.frame) {

            //in frame ~ animate away
            //I play a sound to give the animation some life

            self.centerYInflection.constant = aPointAboveScene
            self.centerYInflection.priority = UILayoutPriority(950)

        } else {

            //I play a different sound just to keep the user engaged
            //out of frame ~ animate into scene
            self.centerYInflection.constant = 0
            self.centerYInflection.priority = UILayoutPriority(950)
            self.view.setNeedsLayout()
            self.view.layoutIfNeeded()
         }) { (success) -> Void in

            //do something else

        }
    }
}

Много неправильных поворотов

Эти заметки на самом деле представляют собой набор советов, которые я написал для себя. Я лично и болезненно все делал. Надеюсь, это руководство пощадит других.

  1. Остерегайтесь zPositioning. Иногда, когда очевидно, что ничего не происходит, вам следует скрыть некоторые другие представления или использовать отладчик представлений, чтобы найти ваше анимированное представление. Я даже встречал случаи, когда определяемый пользователем атрибут времени выполнения терялся в xml раскадровки и приводил к закрытию анимированного представления (во время работы).

  2. Всегда найдите минутку, чтобы прочитать документацию (новую и старую), Быструю помощь и заголовки. Apple продолжает вносить множество изменений, чтобы лучше управлять ограничениями AutoLayout (см. Представления стека). Или, по крайней мере, Поваренная книга AutoLayout. Имейте в виду, что иногда лучшие решения находятся в более старой документации / видео.

  3. Поиграйте со значениями в анимации и рассмотрите возможность использования других вариантов animateWithDuration.

  4. Не указывайте жестко определенные значения макета в качестве критериев для определения изменений других констант, вместо этого используйте значения, которые позволяют вам определять местоположение представления. CGRectContainsRect - один из примеров

  5. Если необходимо, не стесняйтесь использовать поля макета, связанные с представлением, участвующим в определении ограничения let viewMargins = self.webview.layoutMarginsGuide: в примере
  6. Не выполняйте ненужную работу, все представления с ограничениями на раскадровке имеют ограничения, привязанные к свойству self.viewName.constraints
  7. Измените свои приоритеты для любых ограничений на менее 1000. Я установил свои приоритеты на 250 (низкий) или 750 (высокий) на раскадровке; (если вы попытаетесь изменить приоритет 1000 на что-либо в коде, приложение выйдет из строя, потому что требуется 1000)
  8. Не пытайтесь сразу использовать activateConstraints и deactivateConstraints (у них есть свое место, но когда вы просто учитесь или если вы используете раскадровку, их использование, вероятно, означает, что вы делаете слишком много ~ у них есть место, хотя, как показано ниже)
  9. Не используйте addConstraints / removeConstraints, если вы действительно не добавляете новое ограничение в код. Я обнаружил, что в большинстве случаев я размещаю представления в раскадровке с желаемыми ограничениями (помещая представление за пределы экрана), а затем в коде я анимирую ограничения, ранее созданные в раскадровке, чтобы перемещать представление.
  10. Я потратил много времени на создание ограничений с новым классом и подклассами NSAnchorLayout. Они работают отлично, но мне потребовалось время, чтобы понять, что все ограничения, которые мне были нужны, уже существовали в раскадровке. Если вы создаете ограничения в коде, то наверняка используйте этот метод для агрегирования ваших ограничений:

Быстрый пример решений, которых следует ИЗБЕЖАТЬ при использовании раскадровки

private var _nc:[NSLayoutConstraint] = []
    lazy var newConstraints:[NSLayoutConstraint] = {

        if !(self._nc.isEmpty) {
            return self._nc
        }

        let viewMargins = self.webview.layoutMarginsGuide
        let minimumScreenWidth = min(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height)

        let centerY = self.ratingView.centerYAnchor.constraintEqualToAnchor(self.webview.centerYAnchor)
        centerY.constant = -1000.0
        centerY.priority = (950)
        let centerX =  self.ratingView.centerXAnchor.constraintEqualToAnchor(self.webview.centerXAnchor)
        centerX.priority = (950)

        if let buttonConstraints = self.originalRatingViewConstraints?.filter({

            ($0.firstItem is UIButton || $0.secondItem is UIButton )
        }) {
            self._nc.appendContentsOf(buttonConstraints)

        }

        self._nc.append( centerY)
        self._nc.append( centerX)

        self._nc.append (self.ratingView.leadingAnchor.constraintEqualToAnchor(viewMargins.leadingAnchor, constant: 10.0))
        self._nc.append (self.ratingView.trailingAnchor.constraintEqualToAnchor(viewMargins.trailingAnchor, constant: 10.0))
        self._nc.append (self.ratingView.widthAnchor.constraintEqualToConstant((minimumScreenWidth - 20.0)))
        self._nc.append (self.ratingView.heightAnchor.constraintEqualToConstant(200.0))

        return self._nc
    }()

Если вы забудете один из этих советов или более простые, например, куда добавить layoutIfNeeded, скорее всего, ничего не произойдет: в этом случае у вас может получиться наполовину готовое решение, подобное этому:

NB - Найдите минутку, чтобы прочитать раздел AutoLayout ниже и исходное руководство. Есть способ использовать эти методы в дополнение к вашим динамическим аниматорам.

UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 1.0, options: [.CurveEaseOut], animations: { () -> Void in

            //
            if self.starTopInflectionPoint.constant < 0  {
                //-3000
                //offscreen
                self.starTopInflectionPoint.constant = self.navigationController?.navigationBar.bounds.height ?? 0
                self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)

            } else {

                self.starTopInflectionPoint.constant = -3000
                 self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)
            }

        }) { (success) -> Void in

            //do something else
        }

    }

Фрагмент из Руководства по AutoLayout (обратите внимание, что второй фрагмент предназначен для использования OS X). Кстати. Насколько я понимаю, этого больше нет в текущем руководстве. Предпочтительные методы продолжают развиваться.

Анимация изменений, внесенных с помощью автоматического макета

Если вам нужен полный контроль над анимацией изменений, сделанных с помощью Auto Layout, вы должны внести изменения ограничений программно. Базовая концепция одинакова как для iOS, так и для OS X, но есть несколько незначительных отличий.

В приложении для iOS ваш код будет выглядеть примерно так:

[containerView layoutIfNeeded]; // Ensures that all pending layout operations have been completed
[UIView animateWithDuration:1.0 animations:^{
     // Make all constraint changes here
     [containerView layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes
}];

В OS X используйте следующий код при использовании многослойной анимации:

[containterView layoutSubtreeIfNeeded];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
     [context setAllowsImplicitAnimation: YES];
     // Make all constraint changes here
     [containerView layoutSubtreeIfNeeded];
}];

Если вы не используете анимацию на основе слоев, вы должны анимировать константу с помощью аниматора ограничения:

[[constraint animator] setConstant:42];

Для тех, кто лучше учится, посмотрите это раннее видео от Apple.

Обратить особое внимание

Часто в документации есть небольшие примечания или фрагменты кода, которые приводят к более масштабным идеям. Например, прикрепление ограничений автоматической компоновки к динамическим аниматорам - отличная идея.

Удачи и да пребудет с вами Сила.

person Tommie C.    schedule 11.12.2015

Быстрое решение:

yourConstraint.constant = 50
UIView.animate(withDuration: 1.0, animations: {
    yourView.layoutIfNeeded
})
person Nazariy Vlizlo    schedule 11.08.2016

Рабочее решение 100% Swift 5.3

Я прочитал все ответы и хочу поделиться кодом и иерархией строк, которые я использовал во всех своих приложениях, чтобы правильно их анимировать. Некоторые решения здесь не работают, вы должны проверить их на более медленных устройствах, например iPhone 5, в данный момент.

self.btnHeightConstraint.constant = 110
UIView.animate(withDuration: 0.27) { [weak self] in 
     self?.view.layoutIfNeeded()
}
person C0mrade    schedule 12.06.2017

Я пытался анимировать ограничения, и мне было нелегко найти хорошее объяснение.

То, что говорят другие ответы, полностью верно: вам нужно вызвать [self.view layoutIfNeeded]; внутри animateWithDuration: animations:. Однако другой важный момент - иметь указатели для каждого NSLayoutConstraint, которое вы хотите анимировать.

Я создал пример в GitHub.

person Gabriel.Massana    schedule 25.11.2014

Рабочее и только что протестированное решение для Swift 3 с Xcode 8.3.3:

self.view.layoutIfNeeded()
self.calendarViewHeight.constant = 56.0

UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
        self.view.layoutIfNeeded()
    }, completion: nil)

Просто имейте в виду, что self.calendarViewHeight - это ограничение, относящееся к customView (CalendarView). Я вызвал .layoutIfNeeded () в self.view, а НЕ в self.calendarView

Надеюсь на эту помощь.

person Edoardo Vicoli    schedule 19.07.2017

Об этом говорится в статье: http://weblog.invasivecode.com/post/42362079291/auto-layout-and-core-animation-auto-layout-was

В котором он закодировал так:

- (void)handleTapFrom:(UIGestureRecognizer *)gesture {
    if (_isVisible) {
        _isVisible = NO;
        self.topConstraint.constant = -44.;    // 1
        [self.navbar setNeedsUpdateConstraints];  // 2
        [UIView animateWithDuration:.3 animations:^{
            [self.navbar layoutIfNeeded]; // 3
        }];
    } else {
        _isVisible = YES;
        self.topConstraint.constant = 0.;
        [self.navbar setNeedsUpdateConstraints];
        [UIView animateWithDuration:.3 animations:^{
            [self.navbar layoutIfNeeded];
        }];
    }
}

Надеюсь, это поможет.

person D.D.    schedule 13.09.2013

В контексте анимации ограничения я хотел бы упомянуть конкретную ситуацию, когда я сразу же анимировал ограничение в уведомлении keyboard_opened.

Ограничение определяет верхнее пространство от текстового поля до верха контейнера. После открытия клавиатуры я просто делю константу на 2.

Мне не удалось добиться постоянной плавной анимации ограничения непосредственно в уведомлении с клавиатуры. Примерно в половине случаев вид просто перескакивает в новое положение - без анимации.

Мне пришло в голову, что в результате открытия клавиатуры может произойти некоторая дополнительная раскладка. Добавление простого блока dispatch_after с задержкой 10 мс заставляло анимацию запускаться каждый раз - никаких прыжков.

person ivanferdelja    schedule 15.04.2016

person    schedule
comment
это единственный правильный ответ среди других популярных ответов - person Paul T.; 30.10.2020