Bagaimana cara menghidupkan perubahan kendala?

959

Saya memperbarui aplikasi lama dengan AdBannerViewdan ketika tidak ada iklan, itu akan hilang dari layar. Ketika ada iklan, iklan itu akan muncul di layar. Hal-hal dasar.

Gaya lama, saya mengatur bingkai di blok animasi. Gaya baru, saya memiliki IBOutletbatasan tata letak otomatis yang menentukan Yposisi, dalam hal ini jarak dari bagian bawah superview, dan memodifikasi konstanta:

- (void)moveBannerOffScreen {
    [UIView animateWithDuration:5 animations:^{
        _addBannerDistanceFromBottomConstraint.constant = -32;
    }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen {
    [UIView animateWithDuration:5 animations:^{
        _addBannerDistanceFromBottomConstraint.constant = 0;
    }];
    bannerIsVisible = TRUE;
}

Dan spanduk bergerak, persis seperti yang diharapkan, tetapi tidak ada animasi.


UPDATE: Saya menonton kembali WWDC 12 berbicara Best Practices for Mastering Auto Layout yang mencakup animasi. Ini membahas cara memperbarui kendala menggunakan CoreAnimation :

Saya sudah mencoba dengan kode berikut, tetapi mendapatkan hasil yang sama persis:

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

Di samping catatan, saya telah memeriksa beberapa kali dan ini sedang dieksekusi di utas utama .

DBD
sumber
11
Saya belum pernah melihat begitu banyak suara yang ditawarkan untuk pertanyaan dan jawaban pada kesalahan ketik pada SO sebelumnya
abbood
3
Jika ada salah ketik dalam jawaban, Anda harus mengedit jawabannya. Itu sebabnya mereka dapat diedit.
i_am_jorf
@ jeffamaphone - Akan lebih berguna jika Anda menunjukkan kesalahan ketik jadi saya tahu di mana kesalahannya. Anda dapat mengedit jawabannya sendiri dan memperbaiki kesalahan ketik yang menyelamatkan orang lain dari cacian kami. Saya baru saja mengeditnya untuk menghapus konstanta dari blok animasi, jika itu yang Anda maksud.
DBD
1
Saya tidak tahu apa kesalahan ketik itu. Saya menanggapi komentar di atas.
i_am_jorf
9
Maka kesalahan ketik adalah pertanyaannya. Bodohnya saya mengetik "setNeedsLayout" bukannya "layoutIfNeeded". Itu ditampilkan dengan jelas dalam pertanyaan saya ketika saya memotong dan menempelkan kode saya dengan kesalahan dan tangkapan layar dengan perintah yang benar. Namun sepertinya tidak bisa menyadarinya sampai seseorang menunjukkannya.
DBD

Jawaban:

1697

Dua catatan penting:

  1. Anda perlu menelepon layoutIfNeededdalam blok animasi. Apple sebenarnya merekomendasikan Anda untuk memanggilnya sekali sebelum blok animasi untuk memastikan bahwa semua operasi tata letak yang tertunda telah selesai

  2. Anda perlu menyebutnya secara khusus pada tampilan induk (mis. self.view), Bukan tampilan anak yang memiliki batasan yang melekat padanya. Melakukan hal itu akan memperbarui semua pandangan terbatas, termasuk menjiwai pandangan lain yang mungkin dibatasi pada pandangan bahwa Anda mengubah batasan (misalnya B melihat melekat pada bagian bawah Tampilan A dan Anda baru saja mengubah offset atas Tampilan A dan Anda ingin Lihat B untuk menghidupkan dengan itu)

Coba ini:

Objektif-C

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

Cepat 3

UIView.animate(withDuration: 5) {
    self._addBannerDistanceFromBottomConstraint.constant = 0
    self.view.layoutIfNeeded()
}
g3rv4
sumber
200
Anda tahu apa ... jawaban Anda berhasil. WWDC berfungsi .... visi saya gagal. Untuk beberapa alasan, saya membutuhkan waktu seminggu untuk menyadari bahwa saya menelepon setNeedsLayoutdaripada menelepon layoutIfNeeded. Saya sedikit ngeri dengan berapa jam yang saya habiskan tanpa memperhatikan saya baru saja mengetik nama metode yang salah.
DBD
23
Solusinya berfungsi tetapi Anda tidak perlu mengubah konstanta kendala di dalam blok animasi. Benar-benar baik untuk mengatur kendala sekali sebelum memulai animasi. Anda harus mengedit jawaban Anda.
Ortwin Gentz
74
Ini tidak bekerja untuk saya pada awalnya dan kemudian saya menyadari Anda perlu memanggil layoutIfNeeded pada tampilan PARENT, bukan tampilan kendala yang berlaku.
Oliver Pearmain
20
menggunakan layoutIfNeeded akan menghidupkan semua subview menyegarkan bukan hanya perubahan kendala. Bagaimana cara Anda menghidupkan perubahan kendala saja?
ngb
11
"Apple benar-benar merekomendasikan Anda memanggilnya sekali sebelum blok animasi untuk memastikan bahwa semua operasi tata letak yang tertunda telah selesai", terima kasih, tidak pernah memikirkannya, tetapi itu masuk akal.
Rick van der Linde
109

Saya menghargai jawaban yang diberikan, tetapi saya pikir akan lebih baik untuk membawanya sedikit lebih jauh.

Animasi blok dasar dari dokumentasi

[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
}];

tapi sungguh ini skenario yang sangat sederhana. Bagaimana jika saya ingin menghidupkan kendala subview melalui updateConstraintsmetode ini?

Blok animasi yang memanggil metode pembaruan tampilan kendala

[self.view layoutIfNeeded];
[self.subView setNeedsUpdateConstraints];
[self.subView updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionLayoutSubviews animations:^{
    [self.view layoutIfNeeded];
} completion:nil];

Metode updateConstraints ditimpa dalam subkelas UIView dan harus memanggil super di akhir metode.

- (void)updateConstraints
{
    // Update some constraints

    [super updateConstraints];
}

Panduan AutoLayout menyisakan banyak yang diinginkan tetapi layak dibaca. Saya sendiri menggunakan ini sebagai bagian dari UISwitchyang mengubah subview dengan sepasang UITextFields dengan animasi runtuh sederhana dan halus (panjang 0,2 detik). Kendala untuk subview sedang ditangani dalam metode updateConstraint subclass UIView seperti yang dijelaskan di atas.

Cameron Lowell Palmer
sumber
Saat memanggil semua metode di atas self.view(dan bukan pada subview dari tampilan itu), panggilan updateConstraintsIfNeededtidak diperlukan (karena setNeedsLayoutjuga pemicu updateConstraintsdari view itu). Mungkin sepele untuk yang paling, tapi itu bukan untuk saya sampai sekarang;)
anneblue
Sulit untuk berkomentar tanpa mengetahui interaksi penuh Autolayout dan tata letak pegas dan penyangga dalam kasus khusus Anda. Saya berbicara sepenuhnya tentang skenario pembayaran otomatis murni. Saya ingin tahu apa sebenarnya yang terjadi dalam metode layoutSubviews Anda.
Cameron Lowell Palmer
translatesAutoresizingMaskIntoConstraints = TIDAK; Jika Anda ingin autolayout murni, Anda harus menonaktifkan ini.
Cameron Lowell Palmer
Saya belum melihat itu, tapi saya tidak bisa berbicara dengan masalah ini tanpa kode. Mungkin Anda bisa membuat TableView dan menempelkannya di GitHub?
Cameron Lowell Palmer
Maaf, saya tidak menggunakan gitHub :(
anneblue
74

Secara umum, Anda hanya perlu memperbarui batasan dan memanggil layoutIfNeededbagian dalam blok animasi. Ini dapat berupa mengubah .constantproperti dari NSLayoutConstraint, menambahkan batasan penghapusan (iOS 7), atau mengubah .activeproperti batasan (iOS 8 & 9).

Kode sampel:

[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];
}];

Pengaturan sampel:

Project Xcode jadi contoh proyek animasi.

Kontroversi

Ada beberapa pertanyaan tentang apakah batasan harus diubah sebelum blok animasi, atau di dalamnya (lihat jawaban sebelumnya).

Berikut ini adalah percakapan Twitter antara Martin Pilkington yang mengajar iOS, dan Ken Ferry yang menulis Auto Layout. Ken menjelaskan bahwa meskipun mengubah konstanta di luar blok animasi saat ini dapat berfungsi, itu tidak aman dan mereka harus benar-benar berubah di dalam blok animasi. https://twitter.com/kongtomorrow/status/440627401018466305

Animasi:

Proyek Sampel

Berikut adalah proyek sederhana yang menunjukkan bagaimana tampilan dapat dianimasikan. Itu menggunakan Objective C dan menjiwai tampilan dengan mengubah .activeproperti dari beberapa kendala. https://github.com/shepting/SampleAutoLayoutAnimation

Steven Hepting
sumber
+1 untuk menunjukkan contoh menggunakan bendera aktif yang baru. Juga, mengubah batasan di luar blok animasi selalu terasa seperti retasan bagi saya
Korey Hinton
Saya mengangkat masalah di github karena solusi ini tidak berfungsi untuk memindahkan tampilan kembali ke tempat semula. Saya percaya mengubah aktif bukanlah solusi yang tepat dan Anda harus mengubah prioritasnya. Atur bagian atas ke 750, dan bagian bawah ke 250, lalu dalam kode alternatif antara UILayoutPriorityDefaultHigh dan UILayoutPriorityDefaultLow.
Malhal
Alasan lain untuk memperbarui di dalam blok adalah bahwa ia mengisolasi perubahan dari kode panggilan yang mungkin terjadi juga memanggil layoutIfNeeded. (dan terima kasih atas tautan Twitter)
Chris Conover
1
Jawaban yang bagus Khususnya karena Anda menyebutkan percakapan Martin dan Ken tentang ini.
Jona
Saya bingung tentang cara memperbarui batasan dan menulis ini . Bisakah Anda melihatnya?
Sayang
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];
}];
John Erck
sumber
29

Solusi Swift 4

UIView.animate

Tiga langkah sederhana:

  1. Ubah kendala, misalnya:

    heightAnchor.constant = 50
  2. Katakan pada yang mengandung viewbahwa tata letaknya kotor dan bahwa autolayout harus menghitung ulang tata letak:

    self.view.setNeedsLayout()
  3. Dalam blok animasi beri tahu tata letak untuk menghitung ulang tata letak, yang setara dengan pengaturan bingkai secara langsung (dalam hal ini autolayout akan mengatur bingkai):

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

Contoh lengkap yang paling sederhana:

heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
    self.view.layoutIfNeeded()
}

Sidenote

Ada langkah 0 opsional - sebelum mengubah batasan Anda mungkin ingin memanggil self.view.layoutIfNeeded()untuk memastikan bahwa titik awal untuk animasi adalah dari negara dengan batasan lama diterapkan (jika ada beberapa perubahan kendala lain yang tidak boleh dimasukkan dalam animasi ):

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

Karena dengan iOS 10 kami mendapat mekanisme animasi baru - UIViewPropertyAnimator, kita harus tahu bahwa pada dasarnya mekanisme yang sama berlaku untuk itu. Langkah-langkahnya pada dasarnya sama:

heightAnchor.constant = 50
self.view.setNeedsLayout()
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
animator.addAnimations {
    self.view.layoutIfNeeded()
}
animator.startAnimation()

Karena animatorini adalah enkapsulasi dari animasi, kita dapat menyimpan referensi untuk itu dan menyebutnya nanti. Namun, karena di blok animasi kami hanya memberitahu autolayout untuk menghitung ulang frame, kami harus mengubah batasan sebelum memanggil startAnimation. Karena itu sesuatu seperti ini dimungkinkan:

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

Urutan untuk mengubah batasan dan memulai animator adalah penting - jika kita hanya mengubah batasan dan meninggalkan animator kita untuk beberapa saat kemudian, siklus redraw berikutnya dapat meminta perhitungan ulang autolayout dan perubahan tidak akan dianimasikan.

Juga, ingatlah bahwa animator tunggal tidak dapat digunakan kembali - setelah Anda menjalankannya, Anda tidak dapat "menjalankannya kembali". Jadi saya kira tidak ada alasan yang tepat untuk menjaga animator tetap ada, kecuali kita menggunakannya untuk mengendalikan animasi interaktif.

Milan Nosáľ
sumber
👍sangat bagus di swift 5 juga
spnkr
layoutIfNeeded () adalah kuncinya
Medhi
15

Storyboard, Kode, Tips dan beberapa Gotcha

Jawaban lainnya baik-baik saja tetapi yang ini menyoroti beberapa gotcha yang cukup penting dari kendala animasi menggunakan contoh terbaru. Saya telah melalui banyak variasi sebelum saya menyadari hal berikut:

Buat batasan yang ingin Anda targetkan ke variabel Kelas untuk menyimpan referensi yang kuat. Di Swift saya menggunakan variabel malas:

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

Setelah beberapa percobaan saya mencatat bahwa satu HARUS mendapatkan kendala dari tampilan DI ATAS (alias superview) dua pandangan di mana kendala didefinisikan. Dalam contoh di bawah ini (MNGStarRating dan UIWebView adalah dua jenis item yang saya buat menjadi penghalang, dan mereka adalah subview dalam self.view).

Filter Chaining

Saya memanfaatkan metode filter Swift untuk memisahkan batasan yang diinginkan yang akan berfungsi sebagai titik belok. Satu juga bisa menjadi jauh lebih rumit tetapi filter melakukan pekerjaan yang bagus di sini.

Kendala Animasi Menggunakan Swift

Nota Bene - Contoh ini adalah solusi storyboard / kode dan menganggap seseorang telah membuat batasan default di storyboard. Satu kemudian dapat menganimasikan perubahan menggunakan kode.

Dengan asumsi Anda membuat properti untuk difilter dengan kriteria yang akurat dan sampai ke titik belok spesifik untuk animasi Anda (tentu saja Anda juga bisa memfilter untuk array dan loop melalui jika Anda memerlukan beberapa kendala):

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

....

Lain kali...

@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

        }
    }
}

Banyak belokan yang salah

Catatan-catatan ini benar-benar seperangkat kiat yang saya tulis sendiri. Saya melakukan semua yang tidak boleh dilakukan secara pribadi dan menyakitkan. Semoga panduan ini dapat membantu orang lain.

  1. Watch out for zPosisi. Terkadang ketika tidak ada yang tampaknya terjadi, Anda harus menyembunyikan beberapa tampilan lain atau menggunakan tampilan debugger untuk menemukan tampilan animasi Anda. Saya bahkan telah menemukan kasus di mana Atribut Runtime yang Ditentukan Pengguna hilang dalam xml storyboard dan menyebabkan tampilan animasi yang tercakup (saat bekerja).

  2. Selalu luangkan waktu sebentar untuk membaca dokumentasi (baru dan lama), Bantuan Cepat, dan tajuk. Apple terus membuat banyak perubahan untuk mengelola kendala AutoLayout dengan lebih baik (lihat tampilan tumpukan). Atau setidaknya Cookbook AutoLayout . Ingatlah bahwa kadang-kadang solusi terbaik ada di dokumentasi / video yang lebih lama.

  3. Bermain-main dengan nilai-nilai dalam animasi dan pertimbangkan untuk menggunakan varian animateWithDuration lainnya.

  4. Jangan hardcode nilai tata letak khusus sebagai kriteria untuk menentukan perubahan konstanta lain, alih-alih gunakan nilai yang memungkinkan Anda untuk menentukan lokasi tampilan. CGRectContainsRectadalah salah satu contohnya

  5. Jika perlu, jangan ragu untuk menggunakan margin tata letak yang terkait dengan tampilan yang berpartisipasi dalam definisi kendala let viewMargins = self.webview.layoutMarginsGuide: misalnya
  6. Jangan lakukan pekerjaan yang tidak harus Anda lakukan, semua tampilan dengan kendala pada storyboard memiliki kendala yang melekat pada properti itu sendiri. ViewName.constraints
  7. Ubah prioritas Anda untuk batasan apa pun menjadi kurang dari 1000. Saya menetapkan tambang saya menjadi 250 (rendah) atau 750 (tinggi) di storyboard; (jika Anda mencoba mengubah prioritas 1000 untuk apa pun dalam kode maka aplikasi akan macet karena diperlukan 1000)
  8. Pertimbangkan untuk tidak segera mencoba menggunakan activConstraints dan menonaktifkanConstraints (mereka memiliki tempat tetapi ketika baru belajar atau jika Anda menggunakan storyboard menggunakan ini mungkin berarti Anda melakukan terlalu banyak ~ mereka memiliki tempat meskipun seperti yang terlihat di bawah)
  9. Pertimbangkan untuk tidak menggunakan addConstraints / removeConstraints kecuali Anda benar-benar menambahkan kendala baru dalam kode. Saya menemukan bahwa sebagian besar waktu saya tata letak pandangan di storyboard dengan kendala yang diinginkan (menempatkan tampilan offscreen), kemudian dalam kode, saya menghidupkan kendala yang sebelumnya dibuat di storyboard untuk memindahkan pandangan sekitar.
  10. Saya menghabiskan banyak waktu untuk membangun batasan dengan kelas dan subkelas NSAnchorLayout yang baru. Ini bekerja dengan baik tapi butuh beberapa saat untuk menyadari bahwa semua kendala yang saya butuhkan sudah ada di storyboard. Jika Anda membuat batasan dalam kode maka tentu saja menggunakan metode ini untuk mengagregasi kendala Anda:

Contoh Cepat Solusi untuk DIHINDARI saat menggunakan Storyboard

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

Jika Anda lupa salah satu dari tips ini atau yang lebih sederhana seperti di mana menambahkan layoutIfNeeded, kemungkinan besar tidak akan terjadi: Dalam hal ini Anda mungkin memiliki solusi setengah matang seperti ini:

NB - Luangkan waktu sejenak untuk membaca Bagian AutoLayout Di Bawah ini dan panduan asli. Ada cara untuk menggunakan teknik ini untuk menambah Animator Dinamis Anda.

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
        }

    }

Cuplikan dari Panduan AutoLayout (perhatikan bahwa cuplikan kedua adalah untuk menggunakan OS X). BTW - Ini tidak lagi dalam panduan saat ini sejauh yang saya bisa lihat. Teknik yang disukai terus berkembang.

Animasi Perubahan yang Dibuat oleh Tata Letak Otomatis

Jika Anda membutuhkan kontrol penuh atas perubahan yang dibuat oleh Auto Layout, Anda harus membuat perubahan kendala secara terprogram. Konsep dasarnya sama untuk iOS dan OS X, tetapi ada beberapa perbedaan kecil.

Di aplikasi iOS, kode Anda akan terlihat seperti berikut:

[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
}];

Di OS X, gunakan kode berikut saat menggunakan animasi yang didukung layer:

[containterView layoutSubtreeIfNeeded];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
     [context setAllowsImplicitAnimation: YES];
     // Make all constraint changes here
     [containerView layoutSubtreeIfNeeded];
}];

Saat Anda tidak menggunakan animasi yang didukung layer, Anda harus menghidupkan konstanta menggunakan animator kendala:

[[constraint animator] setConstant:42];

Bagi mereka yang belajar lebih baik secara visual, lihat video awal ini dari Apple .

Perhatikan baik-baik

Seringkali dalam dokumentasi ada catatan kecil atau potongan kode yang mengarah ke ide yang lebih besar. Misalnya melampirkan batasan tata letak otomatis ke animator dinamis adalah ide besar.

Semoga Sukses dan Semoga The Force menyertai Anda.

Tommie C.
sumber
12

Solusi cepat:

yourConstraint.constant = 50
UIView.animate(withDuration: 1.0, animations: {
    yourView.layoutIfNeeded
})
Nazariy Vlizlo
sumber
7

Solusi Pengoperasian 100% Swift 3.1

saya telah membaca semua jawaban dan ingin membagikan kode dan hierarki baris yang telah saya gunakan di semua aplikasi saya untuk menghidupkannya dengan benar, Beberapa solusi di sini tidak berfungsi, Anda harus memeriksanya di perangkat yang lebih lambat misalnya iPhone 5 saat ini.

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.
}
Cmmrade
sumber
4

Saya mencoba untuk menghidupkan kendala dan tidak mudah menemukan penjelasan yang bagus.

Apa yang dikatakan jawaban lain sepenuhnya benar: Anda perlu menelepon ke [self.view layoutIfNeeded];dalam animateWithDuration: animations:. Namun, poin penting lainnya adalah memiliki petunjuk untuk setiap NSLayoutConstraintAnda ingin menghidupkan.

Saya membuat contoh di GitHub .

Gabriel.Massana
sumber
4

Solusi yang berfungsi dan baru diuji untuk Swift 3 dengan 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)

Perlu diingat bahwa self.calendarViewHeight adalah kendala yang disebut customView (CalendarView). Saya menelepon .layoutIfNeeded () di self.view dan BUKAN pada self.calendarView

Semoga bantuan ini.

Edoardo Vicoli
sumber
3

Ada artikel yang membicarakan hal ini: http://weblog.invasivecode.com/post/42362079291/auto-layout-and-core-animation-auto-layout-was

Di mana, dia berkode seperti ini:

- (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];
        }];
    }
}

Semoga ini bisa membantu.

DD
sumber
1

Dalam konteks animasi kendala, saya ingin menyebutkan situasi tertentu di mana saya animasi kendala segera dalam notifikasi keyboard_opened.

Batasan mendefinisikan ruang atas dari bidang teks ke atas wadah. Setelah pembukaan keyboard, saya hanya membagi konstanta dengan 2.

Saya tidak dapat mencapai animasi kendala halus yang terus-menerus secara langsung dalam notifikasi keyboard. Sekitar setengah kali tampilan hanya akan melompat ke posisi barunya - tanpa menjiwai.

Terjadi pada saya mungkin ada beberapa tata letak tambahan terjadi sebagai akibat dari pembukaan keyboard. Menambahkan blok dispatch_after sederhana dengan penundaan 10 ms membuat animasi berjalan setiap waktu - tidak ada lompatan.

ivanferdelja
sumber