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


959

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

По старому стилю я установил кадр в анимационном блоке. В новом стиле у меня есть ограничение IBOutletauto-layout, которое определяет 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 «Лучшие практики для освоения автоматической компоновки», который охватывает анимацию. Здесь обсуждается, как обновить ограничения с помощью 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;
}

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


11
Я никогда не видел так много голосов, предложенных за вопрос и ответ на опечатку на SO
abbood

3
Если в ответе есть опечатка, вы должны отредактировать ответ. Вот почему они доступны для редактирования.
i_am_jorf

@jeffamaphone - было бы более полезно, если бы вы указали на опечатку, чтобы я знал, где была ошибка. Вы можете отредактировать ответ самостоятельно и исправить опечатку, сохранив всем остальную часть нашего архива. Я просто отредактировал его, чтобы удалить константу из блока анимации, если это то, на что вы ссылались.
DBD

1
Я не знаю, что это за опечатка. Я отвечал на комментарии выше.
i_am_jorf

9
Тогда опечатка это вопрос. Тупо я набирал «setNeedsLayout» вместо «layoutIfNeeded». Это ясно показано в моем вопросе, когда я вырезал и вставил свой код с ошибкой и снимками экрана с правильной командой. И все же не мог заметить этого, пока кто-то не указал на это.
DBD

Ответы:


1697

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

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

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

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

Objective-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;
}

Свифт 3

UIView.animate(withDuration: 5) {
    self._addBannerDistanceFromBottomConstraint.constant = 0
    self.view.layoutIfNeeded()
}

200
Вы знаете, что ... ваш ответ работает. WWDC работает .... мое зрение не работает. По какой-то причине мне потребовалась неделя, чтобы понять, что я звоню setNeedsLayoutвместо layoutIfNeeded. Я немного испуган тем, сколько часов я провел, не замечая, что я просто набрал неправильное имя метода.
DBD

23
Решение работает, но вам не нужно изменять константу ограничения в блоке анимации. Вполне нормально установить ограничение один раз, прежде чем запустить анимацию. Вы должны отредактировать свой ответ.
Ортвин Генц

74
Сначала это не сработало для меня, и тогда я понял, что вам нужно вызывать layoutIfNeeded в представлении PARENT, а не в представлении, к которому применяются ограничения.
Оливер Пирмейн

20
Использование layoutIfNeeded будет анимировать все обновления подпредставления, а не только изменение ограничения. как анимировать только изменение ограничения?
NGB

11
«Apple фактически рекомендует вам вызывать его один раз перед блоком анимации, чтобы убедиться, что все ожидающие операции макета были завершены», спасибо, никогда не задумывался об этом, но это имеет смысл.
Рик ван дер Линде

109

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

Основной блок анимации из документации

[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
}];

но на самом деле это очень упрощенный сценарий. Что, если я хочу анимировать ограничения subview с помощью 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 оставляет желать лучшего, но его стоит прочитать. Я сам использую это как часть объекта, UISwitchкоторый переключает подпредставление с парой UITextFields с простой и тонкой анимацией коллапса (длиной 0,2 секунды). Ограничения для подпредставления обрабатываются в методах updateConstraints подклассов UIView, как описано выше.


При вызове всех методов, указанных выше self.view(а не в подпредставлении этого представления), вызов updateConstraintsIfNeededне требуется (поскольку setNeedsLayoutтакже запускаются updateConstraintsэти представления). Может быть, тривиально для большинства, но это было не для меня до сих пор;)
anneblue

Трудно комментировать, не зная полного взаимодействия Autolayout и компоновки пружин и распорок в вашем конкретном случае. Я говорю исключительно о сценарии с автопоставкой. Мне было бы интересно узнать, что именно происходит в вашем методе layoutSubviews.
Кэмерон Лоуэлл Палмер

translatesAutoresizingMaskIntoConstraints = NO; Если вы хотите использовать чистую автопоставку, вы обязательно должны отключить это.
Кэмерон Лоуэлл Палмер

Я не видел этого, но я не могу говорить об этой проблеме без некоторого кода. Может быть, вы могли бы макет TableView и прикрепить его на GitHub?
Кэмерон Лоуэлл Палмер

Извините, я не пользуюсь gitHub :(
anneblue

74

Как правило, вам просто нужно обновить ограничения и вызвать 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];
}];

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

Проект XCode, так пример проекта анимации.

полемика

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

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

Анимация:

Пример проекта

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


+1 для показа примера использования нового активного флага. Кроме того, изменение ограничения за пределами блока анимации всегда было для меня хаком
Кори Хинтон,

Я поднял проблему в github, потому что это решение не работает для перемещения представления туда, где оно было. Я считаю, что изменение активности не является правильным решением, и вы должны вместо этого изменить приоритет. Установите верхнюю часть на 750, а нижнюю на 250, затем в коде чередуйте UILayoutPriorityDefaultHigh и UILayoutPriorityDefaultLow.
Малхал

Другая причина обновления внутри блока состоит в том, что он изолирует изменения от вызывающего кода, который также может произойти при вызове layoutIfNeeded. (и спасибо за ссылку в Твиттере)
Крис Коновер

1
Отличный ответ. Особенно потому, что вы упоминаете разговор Мартина и Кена об этом.
Jona

Я запутался в манере обновления ограничений и написал это . Можете ли вы взглянуть?
Мед

36
// Step 1, update your constraint
self.myOutletToConstraint.constant = 50; // New height (for example)

// Step 2, trigger animation
[UIView animateWithDuration:2.0 animations:^{

    // Step 3, call layoutIfNeeded on your animated view's parent
    [self.view layoutIfNeeded];
}];

29

Swift 4 решение

UIView.animate

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

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

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

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

    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()

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

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


Отлично работает и в Swift 5
spnkr

layoutIfNeeded () является ключом
Medhi

15

Раскадровка, код, советы и несколько ошибок

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

Установите ограничения, на которые вы хотите ориентироваться, в переменные класса, чтобы они содержали сильную ссылку. В 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

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. Остерегайтесь zPosition. Иногда, когда ничего не происходит, вы должны скрыть некоторые другие виды или использовать отладчик, чтобы найти анимированный вид. Я даже обнаружил случаи, когда определяемый пользователем атрибут времени выполнения терялся в 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. Не стоит сразу пытаться использовать activConstraints и 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 .

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

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

Удачи и да пребудет с тобой Сила.



7

Рабочий раствор 100% Swift 3.1

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

self.view.layoutIfNeeded() // Force lays of all subviews on root view
UIView.animate(withDuration: 0.5) { [weak self] in // allowing to ARC to deallocate it properly
       self?.tbConstraint.constant = 158 // my constraint constant change
       self?.view.layoutIfNeeded() // Force lays of all subviews on root view again.
}

4

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

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

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


4

Рабочее и только что протестированное решение для 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

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


3

Есть статья об этом: 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];
        }];
    }
}

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


1

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

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.