Як оживити зміни обмежень?


959

Я оновлюю старий додаток 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 найкращі практики для оволодіння автоматичною компонуванням, яка охоплює анімацію. У ньому обговорюється спосіб оновлення обмежень за допомогою 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
Я ніколи не бачив стільки голосів, запропонованих за запитання та відповіді на друкарські запитання на
ТА

3
Якщо у відповіді є помилка друку, слід змінити відповідь. Ось чому їх можна редагувати.
i_am_jorf

@jeffamaphone - Було б корисніше, якби ти вказав на друкарську помилку, щоб я знав, де помилка. Ви самі могли відредагувати відповідь і виправити друкарську помилку, врятувавши всіх інших наших діатриб. Я просто відредагував його, щоб видалити константу з анімаційного блоку, якщо саме це ви мали на увазі.
ДБД

1
Я не знаю, що це за друк. Я відповідав на коментарі вище.
i_am_jorf

9
Тоді помилка друку - це питання. Я тупо вводив "setNeedsLayout" замість "layoutIfNeeded". Це чітко відображається в моєму запитанні, коли я вирізав і вставив свій код із помилкою та знімки екрана з правильною командою. Але, здавалося б, це не помічало, поки хтось не зазначив це.
DBD

Відповіді:


1696

Дві важливі примітки:

  1. Вам потрібно зателефонувати layoutIfNeededвсередині блоку анімації. Apple фактично рекомендує викликати його один раз перед блоком анімації, щоб переконатися, що всі очікувані операції з компонуванням завершені

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

Спробуйте це:

Ціль-С

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

199
Ви знаєте, що ... ваша відповідь працює. 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
}];

але насправді це дуже спрощений сценарій. Що робити, якщо я хочу анімувати обмеження субпрогляду за допомогою updateConstraintsметоду?

Блок анімації, який викликає метод subviews 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 і повинен викликати супер в кінці методу.

- (void)updateConstraints
{
    // Update some constraints

    [super updateConstraints];
}

Посібник з автоматичного розкладу залишає бажати кращого, але його варто прочитати. Я сам використовую це як частину, UISwitchяка перемикає підзагляд з парою UITextFields за допомогою простої та тонкої анімації колапсу (довжина 0,2 секунди). Обмеження для субперегляду обробляються методами updateConstraints для підкласів UIView, як описано вище.


Коли self.viewвикликуєте всі вищевказані методи (а не на підперегляд цього виду), виклик updateConstraintsIfNeededне потрібен (тому що setNeedsLayoutтакож спрацьовує updateConstraintsдля цього виду). Може бути банальним для більшості, але це не було для мене досі;)
anneblue

Важко коментувати, не знаючи повної взаємодії Autolayout та компонування пружин та стойок у вашому конкретному випадку. Я говорю повністю про чистий сценарій автоматичного розкладу. Мені буде цікаво дізнатися, що саме відбувається у вашому методі layoutSubviews.
Камерон Лоуелл Палмер

translatesAutoresizingMaskIntoConstraints = НІ; Якщо ви хочете виконати автоматичну розкладку, ви обов'язково відключите це.
Камерон Лоуелл Палмер,

Я цього не бачив, але я не можу реально говорити з проблемою без якогось коду. Можливо, ви могли б знущатися над 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 Project - зразок анімаційного проекту.

Суперечки

Є деякі запитання щодо того, чи слід змінювати обмеження перед блоком анімації чи всередині нього (див. Попередні відповіді).

Далі йде розмова в Twitter між Мартіном Пілкінгтоном, який викладає iOS, та Кеном Феррі, який написав автоматичний макет. Кен пояснює, що хоча зміна констант поза блоком анімації в даний час може працювати, це не безпечно, і вони дійсно повинні бути змінені всередині блоку анімації. https://twitter.com/kongtomorrow/status/440627401018466305

Анімація:

Зразок проекту

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


+1 для показу прикладу з використанням нового активного прапора. Крім того, зміна обмеження поза блоком анімації для мене завжди відчувала себе злому
Korey Hinton

Я порушив проблему в Github, оскільки це рішення не працює для переміщення погляду назад туди, де він був. Я вважаю, що зміна активного не є правильним рішенням, і вам слід змінити пріоритет. Встановіть верхню до 750, а нижню - 250, а потім у коді чергуйте UILayoutPriorityDefaultHigh та UILayoutPriorityDefaultLow.
маль

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

1
Чудова відповідь. Спеціально тому, що ви згадуєте про це розмову Мартіна та Кена.
Йона

Я розгубився в манері оновлення обмежень і написав це . Ви можете, будь ласка, поглянути?
Мед

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що його макет забруднений і що авторозмітка повинна перерахувати макет:

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

Sidenote

Існує необов'язковий 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()

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

Крім того, пам’ятайте, що одного аніматора не можна використовувати повторно - після запуску його ви не зможете "повторно" запустити. Тож я думаю, що насправді не є вагомою причиною тримати аніматора навколо, якщо ми не використовуємо його для управління інтерактивною анімацією.


👍 також чудово працює у швидкій 5
spnkr

layoutIfNeeded () - ключ
Medhi

15

Дошка розкадрувань, кодекс, підказки та декілька Gotchas

Інші відповіді просто чудові, але цей підкреслює декілька досить важливих проблем анімаційних обмежень, використовуючи останній приклад. Я пережив багато варіацій, перш ніж зрозумів наступне:

Зробіть обмеження, які ви хочете націлити на змінні Class, щоб мати чітке посилання. У 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!
}()

Після декількох експериментів я зазначив , що необхідно отримати обмеження з точки зору ВИЩЕ (інакше SuperView) дві точки зору , де визначено обмеження. У наведеному нижче прикладі (як MNGStarRating, так і UIWebView - це два типи елементів, між якими я створюю обмеження, і вони є підпоглядом у межах самоперегляду).

Ланцюжок фільтрів

Я використовую метод фільтра Свіфта, щоб розділити бажане обмеження, яке буде служити точкою перегину. Можна також отримати набагато складніше, але фільтр робить хорошу роботу тут.

Обмеження анімації за допомогою 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. Іноді, коли нічого, мабуть, не відбувається, слід приховати деякі інші погляди або скористатися налагоджувачем виду, щоб знайти анімований погляд. Я навіть виявив випадки, коли визначений користувачем атрибут Runtime втрачався у 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 та deaktivictConstraints (вони мають своє місце, але коли ви просто навчаєтесь, або якщо ви використовуєте табло, використовуючи це, мабуть, означає, що ви робите занадто багато, у них є місце, хоча, як показано нижче)
  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, швидше за все, нічого не станеться: у такому випадку у вас може бути напівзапечене рішення, як це:

Примітка - Знайдіть хвилину, щоб прочитати розділ Автоматичне розміщення нижче та оригінальний посібник. Існує спосіб використовувати ці методи, щоб доповнити свої динамічні аніматори.

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
        }

    }

Фрагмент з Посібника з автоматичного розкладу (зверніть увагу, другий фрагмент призначений для використання ОС X). BTW - Настільки, наскільки я бачу, це не в поточному посібнику. Кращі методи продовжують розвиватися.

Анімаційні зміни, зроблені автоматичною компоновкою

Якщо вам потрібен повний контроль над анімацією змін, внесених автоматичним розкладом, ви повинні змінити свої обмеження програмно. Основна концепція однакова і для 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 .

Зверніть пильну увагу

Часто в документації зустрічаються невеликі замітки або фрагменти коду, які призводять до більшої ідеї. Наприклад, прикріплення обмежень щодо автоматичного розміщення динамічних аніматорів є великою ідеєю.

Удачі і нехай Сила буде з тобою.



6

Робочий розчин 100% швидкий 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.