mencoba untuk menghidupkan kendala dalam cepat

242

Saya memiliki UITextField yang ingin saya perbesar lebarnya ketika diketuk. Saya mengatur kendala dan memastikan kendala di sebelah kiri memiliki prioritas lebih rendah daripada yang saya coba untuk menghidupkan di sisi kanan.

Berikut adalah kode yang saya coba gunakan.

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

Ini berfungsi, tetapi tampaknya hanya terjadi secara instan dan sepertinya tidak ada gerakan apa pun. Saya mencoba mengaturnya 10 detik untuk memastikan saya tidak melewatkan apa pun, tetapi saya mendapatkan hasil yang sama.

nameInputConstraint adalah nama dari kendala yang saya kontrol seret untuk menghubungkan ke kelas saya dari IB.

Terima kasih atas bantuan Anda sebelumnya!

icekomo
sumber
3
kemungkinan duplikat dari Bagaimana cara menganimasikan perubahan kendala?
emem

Jawaban:

666

Anda harus terlebih dahulu mengubah kendala dan kemudian menghidupkan pembaruan.

self.nameInputConstraint.constant = 8

Cepat 2

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

Swift 3, 4, 5

UIView.animate(withDuration: 0.5) {
    self.view.layoutIfNeeded()
}
Mundi
sumber
2
Adakah yang bisa menjelaskan bagaimana / mengapa ini bekerja? Memanggil animationWithDuration pada setiap UIView akan menghidupkan kelas apa pun yang mewarisi dari UIView dan mengubah nilai fisik?
nipponese
3
API animasi yang Anda sebutkan digunakan untuk menghidupkan properti tampilan dan lapisan. Di sini kita perlu menghidupkan perubahan tata letak . Itulah yang mengubah konstanta panggilan kendala tata letak - mengubah konstanta saja tidak menghasilkan apa-apa.
Mundi
2
@ Jacky Mungkin Anda salah mengira ini dengan alasan Objective-C mengacu pada variabel kuat dan lemah. Penutupan cepat berbeda: setelah penutupan selesai, tidak ada benda yang menahan untuk selfdigunakan dalam penutupan.
Mundi
2
tidak bekerja dalam kasus saya, itu terjadi pertama kali, apa yang harus dilakukan
Shakti
13
Butuh satu jam bagi saya untuk memperhatikan, bahwa saya harus memanggil layoutIf Perlu di superview ...
Nominalista
28

SWIFT 4.x:

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

Contoh dengan selesai:

self.mConstraint.constant = 100
UIView.animate(withDuration: 0.3, animations: {
        self.view.layoutIfNeeded()
    }, completion: {res in
        //Do something
})
Hadži Lazar Pešić
sumber
2
itu tidak berfungsi. Saya telah melakukan ini UIView.animate (withDuration: 10, delay: 0, options: .curveEaseInOut, animasi: {self.leftViewHeightConstraint.constant = 200 self.leftView.layoutIfNeeded ()}, selesai: nil)
saurabhgoyal795
1
Anda harus mengubah batasan dan kemudian menghidupkannya.
JaredH
Sementara cuplikan kode ini mungkin solusinya, termasuk penjelasan sangat membantu untuk meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, dan orang-orang itu mungkin tidak tahu alasan untuk saran kode Anda.
Narendra Jadhav
Baris di atas Membuat hari saya :) Terima kasih @ Hadzi
Nrv
18

Sangat penting untuk menunjukkan bahwa ini hanya view.layoutIfNeeded()berlaku untuk tampilan subviews.

Karena itu untuk menghidupkan batasan tampilan, penting untuk menyebutnya pada view-to-animate superview sebagai berikut:

    topConstraint.constant = heightShift

    UIView.animate(withDuration: 0.3) {

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

Contoh untuk tata letak sederhana sebagai berikut:

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()
        }
    }
}
Stéphane de Luca
sumber
Memperbarui batasan ketinggian pada Swift 4 dan ini berfungsi saat memanggil layoutIfNeeded pada tampilan tetapi tidak superview tidak. Sangat membantu, terima kasih!
Alekxos
Ini benar-benar jawaban yang benar. Jika Anda tidak menyebutnya di superview, tampilan Anda hanya melompat ke lokasi baru. Ini adalah perbedaan yang sangat penting.
Bryan Deemer
11

Dengan Swift 5 dan iOS 12.3, sesuai dengan kebutuhan Anda, Anda dapat memilih salah satu dari 3 cara berikut untuk menyelesaikan masalah Anda.


# 1. Menggunakan UIView's animate(withDuration:animations:)metode kelas

animate(withDuration:animations:) memiliki deklarasi berikut:

Buat animasi perubahan pada satu atau beberapa tampilan menggunakan durasi yang ditentukan.

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

Kode Playground di bawah ini menunjukkan kemungkinan penerapan animate(withDuration:animations:)untuk menganimasikan perubahan konstan kendala Tata Letak Otomatis.

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. Menggunakan UIViewPropertyAnimator's init(duration:curve:animations:)initialiser dan startAnimation()metode

init(duration:curve:animations:) memiliki deklarasi berikut:

Menginisialisasi animator dengan kurva timing UIKit internal.

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

Kode Playground di bawah ini menunjukkan kemungkinan implementasi init(duration:curve:animations:)dan startAnimation()untuk menghidupkan perubahan konstan kendala Layout Otomatis.

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. Menggunakan UIViewPropertyAnimator's runningPropertyAnimator(withDuration:delay:options:animations:completion:)metode kelas

runningPropertyAnimator(withDuration:delay:options:animations:completion:) memiliki deklarasi berikut:

Membuat dan mengembalikan objek animator yang segera mulai menjalankan animasinya.

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

Kode Playground di bawah ini menunjukkan kemungkinan penerapan runningPropertyAnimator(withDuration:delay:options:animations:completion:)untuk menganimasikan perubahan konstan kendala Tata Letak Otomatis.

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()
Imanou Petit
sumber
1
Jawaban yang sangat bagus!
Bashta
@Imanou jawaban yang bagus, hargai detailnya! Mungkin menambahkan deskripsi singkat tentang opsi dan apa perbedaannya? Akan sangat membantu saya dan saya yakin kepada orang lain untuk memiliki kalimat tentang mengapa saya memilih satu dari yang lain.
rayepps
3

Dalam kasus saya, saya hanya memperbarui tampilan khusus.

// 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()
}
Sarang
sumber
-1

Lihat ini .

Video mengatakan bahwa Anda hanya perlu menambahkan self.view.layoutIfNeeded()seperti berikut:

UIView.animate(withDuration: 1.0, animations: {
       self.centerX.constant -= 75
       self.view.layoutIfNeeded()
}, completion: nil)
mossman252
sumber