versuchen, eine Einschränkung schnell zu animieren


242

Ich habe ein UITextField, dessen Breite ich beim Tippen vergrößern möchte. Ich habe die Einschränkungen eingerichtet und sichergestellt, dass die Einschränkung auf der linken Seite die niedrigere Priorität hat als die, die ich auf der rechten Seite animieren möchte.

Hier ist der Code, den ich verwenden möchte.

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

Das funktioniert, aber es scheint sofort zu geschehen und es scheint keine Bewegung zu geben. Ich habe versucht, es auf 10 Sekunden einzustellen, um sicherzustellen, dass mir nichts entgeht, aber ich habe die gleichen Ergebnisse erzielt.

nameInputConstraint ist der Name der Einschränkung, die ich gesteuert habe, um eine Verbindung von IB zu meiner Klasse herzustellen.

Danke im Vorraus für deine Hilfe!


Antworten:


665

Sie müssen zuerst die Einschränkung ändern und dann das Update animieren.

self.nameInputConstraint.constant = 8

Swift 2

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

Swift 3, 4, 5

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

2
Kann jemand erklären, wie / warum das funktioniert? Wenn Sie animationWithDuration in einem UIView aufrufen, wird jede Klasse animiert, die von UIView erbt und einen physischen Wert ändert.
Nipponese

3
Die von Ihnen erwähnte Animations-API wird verwendet, um die Eigenschaften von Ansichten und Ebenen zu animieren . Hier müssen wir die Änderung im Layout animieren . Das ist es, was das Ändern der Konstante einer Layoutbeschränkung erfordert - das Ändern der Konstante allein bewirkt nichts.
Mundi

2
@ Jacky Vielleicht verwechseln Sie dies mit Objective-C-Argumentation in Bezug auf starke und schwache Variablen. Schnelle Verschlüsse sind anders: Sobald der Verschluss fertig ist, hält sich kein Gegenstand mehr an dem selfim Verschluss verwendeten fest.
Mundi

2
In meinem Fall funktioniert es nicht, es passiert zuerst, was zu tun ist
Shakti

13
Es dauerte eine Stunde, bis ich bemerkte, dass ich layoutIfNeeded auf der Übersicht anrufen musste ...
Nominalista

28

SWIFT 4.x:

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

Beispiel mit Abschluss:

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

2
Es funktioniert nicht. Ich habe dies getan. UIView.animate (withDuration: 10, delay: 0, options: .curveEaseInOut, animations: {self.leftViewHeightConstraint.constant = 200 self.leftView.layoutIfNeeded ()}, Vervollständigung: nil)
saurabhgoyal795

1
Sie müssen die Einschränkung ändern und dann animieren.
JaredH

Während dieses Code-Snippet die Lösung sein kann, hilft das Hinzufügen einer Erklärung wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für Leser in Zukunft beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen.
Narendra Jadhav

Die obige Zeile hat meinen Tag gemacht :) Danke @Hadzi
Nrv

18

Es ist sehr wichtig darauf hinzuweisen, dass view.layoutIfNeeded()dies nur für die Ansichtsunteransichten gilt.

Um die Ansichtsbeschränkung zu animieren, ist es daher wichtig, sie in der Ansicht zum Animieren der Ansicht wie folgt aufzurufen :

    topConstraint.constant = heightShift

    UIView.animate(withDuration: 0.3) {

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

Ein Beispiel für ein einfaches Layout wie folgt:

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

Das Aktualisieren einer Höhenbeschränkung in Swift 4 funktionierte beim Aufrufen von layoutIfNeeded für die Ansicht, nicht jedoch für die Übersicht. Wirklich hilfreich, danke!
Alekxos

Das ist wirklich die richtige Antwort. Wenn Sie es in der Übersicht nicht aufrufen, springt Ihre Ansicht einfach zum neuen Speicherort. Dies ist eine sehr wichtige Unterscheidung.
Bryan Deemer

11

Mit Swift 5 und iOS 12.3 können Sie je nach Bedarf eine der drei folgenden Möglichkeiten wählen, um Ihr Problem zu lösen.


# 1. Verwenden UIViewder animate(withDuration:animations:)Klassenmethode von

animate(withDuration:animations:) hat die folgende Erklärung:

Animieren Sie Änderungen an einer oder mehreren Ansichten mit der angegebenen Dauer.

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

Der folgende Spielplatzcode zeigt eine mögliche Implementierung von animate(withDuration:animations:), um die ständige Änderung einer Auto-Layout-Einschränkung zu animieren.

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. Verwenden UIViewPropertyAnimatordes init(duration:curve:animations:)Initialisierers und der startAnimation()Methode

init(duration:curve:animations:) hat die folgende Erklärung:

Initialisiert den Animator mit einer integrierten UIKit-Timing-Kurve.

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

Der folgende Spielplatzcode zeigt eine mögliche Implementierung von init(duration:curve:animations:)und startAnimation()um die ständige Änderung einer Auto-Layout-Einschränkung zu animieren.

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. Verwenden UIViewPropertyAnimatorder runningPropertyAnimator(withDuration:delay:options:animations:completion:)Klassenmethode von

runningPropertyAnimator(withDuration:delay:options:animations:completion:) hat die folgende Erklärung:

Erstellt ein Animator-Objekt und gibt es zurück, dessen Animationen sofort ausgeführt werden.

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

Der folgende Spielplatzcode zeigt eine mögliche Implementierung von runningPropertyAnimator(withDuration:delay:options:animations:completion:), um die ständige Änderung einer Auto-Layout-Einschränkung zu animieren.

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
So eine tolle Antwort!
Bashta

@Imanou tolle Antwort, schätzen das Detail! Fügen Sie möglicherweise eine kurze Beschreibung der Optionen hinzu und was ist der Unterschied? Wäre sehr hilfreich für mich und ich bin mir sicher, dass andere einen Satz darüber haben, warum ich einen über den anderen gewählt habe.
Rayepps

3

In meinem Fall habe ich nur die benutzerdefinierte Ansicht aktualisiert.

// 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

Schau dir das an .

Das Video sagt, dass Sie nur self.view.layoutIfNeeded()wie folgt hinzufügen müssen :

UIView.animate(withDuration: 1.0, animations: {
       self.centerX.constant -= 75
       self.view.layoutIfNeeded()
}, completion: nil)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.