пытаясь оживить ограничение в быстром


242

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

Вот код, который я пытаюсь использовать.

  // move the input box
    UIView.animateWithDuration(10.5, animations: {
        self.nameInputConstraint.constant = 8
        }, completion: {
            (value: Bool) in
            println(">>> move const")
    })

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

nameInputConstraint - это имя ограничения, которое я контролирую, перетаскивающего для подключения к моему классу из IB.

Заранее спасибо за помощь!


Ответы:


666

Вам необходимо сначала изменить ограничение, а затем анимировать обновление.

self.nameInputConstraint.constant = 8

Swift 2

UIView.animateWithDuration(0.5) {
    self.view.layoutIfNeeded()
}

Свифт 3, 4, 5

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

2
Может кто-нибудь объяснить, как / почему это работает? Вызов animationWithDuration в любом UIView будет анимировать любой класс, который наследуется от UIView и меняет физическое значение?
Ниппонский

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

2
@Jacky Возможно, вы ошибаетесь с рассуждениями Objective-C относительно сильных и слабых переменных. Быстродействующие затворы бывают разные: после закрытия затвора нет объекта, удерживающего его selfв затворе.
Mundi

2
не работает в моем случае, это происходит в первую очередь, что делать
Шакти

13
Мне потребовался один час, чтобы заметить, что я должен позвонить layoutIfNeeded на superview ...
Nominalista

28

SWIFT 4.x:

self.mConstraint.constant = 100.0
UIView.animate(withDuration: 0.3) {
        self.view.layoutIfNeeded()
}

Пример с завершением:

self.mConstraint.constant = 100
UIView.animate(withDuration: 0.3, animations: {
        self.view.layoutIfNeeded()
    }, completion: {res in
        //Do something
})

2
это не работает, я сделал это UIView.animate (withDuration: 10, задержка: 0, параметры: .curveEaseInOut, анимации: {self.leftViewHeightConstraint.constant = 200 self.leftView.layoutIfNeeded ()}, завершение: nil)
saurabhgoyal795

1
Вы должны изменить ограничение, а затем анимировать.
Джаред,

Хотя этот фрагмент кода может быть решением, включение объяснения действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин, по которым вы предлагаете код.
Нарендра Джадхав

Вышеуказанная строка сделала мой день :) Спасибо @Hadzi
Nrv

18

Очень важно указать, что это view.layoutIfNeeded()относится только к просмотру подпредставлений.

Поэтому для анимации ограничения представления важно вызвать его в суперпредставлении «просмотр- анимация» следующим образом:

    topConstraint.constant = heightShift

    UIView.animate(withDuration: 0.3) {

        // request layout on the *superview*
        self.view.superview?.layoutIfNeeded()
    }

Пример для простого макета выглядит следующим образом:

class MyClass {

    /// Container view
    let container = UIView()
        /// View attached to container
        let view = UIView()

    /// Top constraint to animate
    var topConstraint = NSLayoutConstraint()


    /// Create the UI hierarchy and constraints
    func createUI() {
        container.addSubview(view)

        // Create the top constraint
        topConstraint = view.topAnchor.constraint(equalTo: container.topAnchor, constant: 0)


        view.translatesAutoresizingMaskIntoConstraints = false

        // Activate constaint(s)
        NSLayoutConstraint.activate([
           topConstraint,
        ])
    }

    /// Update view constraint with animation
    func updateConstraint(heightShift: CGFloat) {
        topConstraint.constant = heightShift

        UIView.animate(withDuration: 0.3) {

            // request layout on the *superview*
            self.view.superview?.layoutIfNeeded()
        }
    }
}

Обновление ограничения высоты в Swift 4, и это работало при вызове layoutIfNeeded для представления, но не для суперпредставления - нет. Действительно полезно, спасибо!
Alekxos

Это действительно правильный ответ. Если вы не позвоните в суперпредставление, ваш вид просто перейдет на новое место. Это очень важное различие.
Брайан Даймер

11

С Swift 5 и iOS 12.3, в соответствии с вашими потребностями, вы можете выбрать один из 3 следующих способов для решения вашей проблемы.


# 1. Использование UIView«S animate(withDuration:animations:)метод класса

animate(withDuration:animations:) имеет следующую декларацию:

Анимируйте изменения в одном или нескольких представлениях, используя указанную продолжительность.

class func animate(withDuration duration: TimeInterval, animations: @escaping () -> Void)

Код Playground ниже показывает возможную реализацию для animate(withDuration:animations:)того, чтобы анимировать постоянное изменение ограничения Auto Layout.

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    let textView = UITextView()
    lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)

    override func viewDidLoad() {
        view.backgroundColor = .white
        view.addSubview(textView)

        textView.backgroundColor = .orange
        textView.isEditable = false
        textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
        heightConstraint.isActive = true

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
        textView.addGestureRecognizer(tapGesture)
    }

    @objc func doIt(_ sender: UITapGestureRecognizer) {
        heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
        UIView.animate(withDuration: 2) {
            self.view.layoutIfNeeded()
        }
    }

}

PlaygroundPage.current.liveView = ViewController()

# 2. Используя UIViewPropertyAnimator«ы init(duration:curve:animations:)инициализатора и startAnimation()метод

init(duration:curve:animations:) имеет следующую декларацию:

Инициализирует аниматор с помощью встроенной временной кривой UIKit.

convenience init(duration: TimeInterval, curve: UIViewAnimationCurve, animations: (() -> Void)? = nil)

Код игровой площадки ниже показывает возможную реализацию init(duration:curve:animations:)и startAnimation()для того, чтобы анимировать постоянное изменение ограничения Auto Layout.

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    let textView = UITextView()
    lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)

    override func viewDidLoad() {
        view.backgroundColor = .white
        view.addSubview(textView)

        textView.backgroundColor = .orange
        textView.isEditable = false
        textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
        heightConstraint.isActive = true

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
        textView.addGestureRecognizer(tapGesture)
    }

    @objc func doIt(_ sender: UITapGestureRecognizer) {
        heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
        let animator = UIViewPropertyAnimator(duration: 2, curve: .linear, animations: {
            self.view.layoutIfNeeded()
        })
        animator.startAnimation()
    }

}

PlaygroundPage.current.liveView = ViewController()

# 3. Использование UIViewPropertyAnimator«S runningPropertyAnimator(withDuration:delay:options:animations:completion:)метод класса

runningPropertyAnimator(withDuration:delay:options:animations:completion:) имеет следующую декларацию:

Создает и возвращает объект аниматора, который сразу же запускает анимацию.

class func runningPropertyAnimator(withDuration duration: TimeInterval, delay: TimeInterval, options: UIViewAnimationOptions = [], animations: @escaping () -> Void, completion: ((UIViewAnimatingPosition) -> Void)? = nil) -> Self

Код Playground ниже показывает возможную реализацию для runningPropertyAnimator(withDuration:delay:options:animations:completion:)того, чтобы анимировать постоянное изменение ограничения Auto Layout.

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    let textView = UITextView()
    lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)

    override func viewDidLoad() {
        view.backgroundColor = .white
        view.addSubview(textView)

        textView.backgroundColor = .orange
        textView.isEditable = false
        textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
        heightConstraint.isActive = true

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
        textView.addGestureRecognizer(tapGesture)
    }

    @objc func doIt(_ sender: UITapGestureRecognizer) {
        heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
        UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 2, delay: 0, options: [], animations: {
            self.view.layoutIfNeeded()
        })
    }

}

PlaygroundPage.current.liveView = ViewController()

1
Такой отличный ответ!
Башта

@ Имману отличный ответ, ценю детали! Возможно добавить краткое описание опций и в чем разница? Было бы очень полезно для меня, и я уверен, что у других есть предложение о том, почему я бы выбрал одно из другого.
rayepps

3

В моем случае я только обновил пользовательский вид.

// DO NOT LIKE THIS
customView.layoutIfNeeded()    // Change to view.layoutIfNeeded()
UIView.animate(withDuration: 0.5) {
   customViewConstraint.constant = 100.0
   customView.layoutIfNeeded() // Change to view.layoutIfNeeded()
}

-1

Смотрите это .

Видео говорит, что вам нужно просто добавить, self.view.layoutIfNeeded()как показано ниже:

UIView.animate(withDuration: 1.0, animations: {
       self.centerX.constant -= 75
       self.view.layoutIfNeeded()
}, completion: nil)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.