намагаючись стрімко оживити обмеження


242

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

Ось код, який я намагаюся використовувати.

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

Це працює, але, здається, це відбувається просто миттєво, і руху, здається, немає. Я намагався встановити це на 10 секунд, щоб переконатися, що я нічого не пропускав, але отримав ті самі результати.

nameInputConstraint - це ім'я обмеження, яке я контролюю перетягуванням для підключення до свого класу з ІБ.

Заздалегідь дякуємо за вашу допомогу!


3
можливий дублікат Як я анімую зміну обмежень?
emem

Відповіді:


666

Спочатку потрібно змінити обмеження, а потім оживити оновлення.

self.nameInputConstraint.constant = 8

Швидкий 2

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

Швидкий 3, 4, 5

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

2
Хтось може пояснити, як / чому це працює? Виклик анімаціїWithDuration на будь-якому UIView буде анімувати будь-який клас, який успадковується від UIView та змінить фізичне значення?
ніппонез

3
API анімації, який ви згадуєте, використовується для анімації властивостей поглядів та шарів. Тут нам потрібно оживити зміни в макеті . Ось для чого потрібна зміна константи обмеження макета - сама зміна константи нічого не робить.
Mundi

2
@Jacky Можливо, ви помиляєтеся з міркуванням Objective-C стосовно посилань на сильні та слабкі змінні. Швидке закриття буває різним: як тільки закриття закінчується, жоден об'єкт не тримається за selfвикористаний у закритті.
Mundi

2
не працюючи в моєму випадку, це відбувається найперше, що робити
Шакти

13
Він взяв мене за одну годину до відомості, що я повинен назвати layoutIfNeeded на надтабліци ...
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 (зDuration: 10, затримка: 0, параметри: .curveEaseInOut, анімації: {self.leftViewHeightConstraint.constant = 200 self.leftView.layoutIfNeeded ()}, завершення: nil)
saurabhgoyal795

1
Ви повинні змінити обмеження, а потім оживити.
ДжаредH

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

Вищенаведений рядок Зробив мій день :) Дякую @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 для перегляду, але не нагляд не робив. Дуже корисно, дякую!
Алексос

Це дійсно правильна відповідь. Якщо ви не зателефонували йому під час нагляду, ваш погляд просто переходить на нове місце. Це дуже важлива відмінність.
Брайан Демер

11

З Swift 5 та iOS 12.3, відповідно до ваших потреб, ви можете вибрати один із трьох наступних способів вирішити свою проблему.


№1. Використання UIView«S animate(withDuration:animations:)метод класу

animate(withDuration:animations:) має таку заяву:

Анімовані зміни до одного або декількох переглядів, використовуючи вказану тривалість.

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

Код ігрового майданчика нижче показує можливу реалізацію для animate(withDuration:animations:)того, щоб оживити постійну зміну обмеження автоматичного макета.

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()з метою оживлення постійної зміни обмеження автоматичного макета.

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

Код ігрового майданчика нижче показує можливу реалізацію для runningPropertyAnimator(withDuration:delay:options:animations:completion:)того, щоб оживити постійну зміну обмеження автоматичного макета.

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
Така чудова відповідь!
Башта

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