setNeedsLayout vs setNeedsUpdateConstraints dan layoutIfNeeded vs updateConstraintsIfNeeded

227

Saya tahu bahwa rantai tata letak otomatis pada dasarnya terdiri dari 3 proses yang berbeda.

  1. memperbarui kendala
  2. tampilan tata letak (di sinilah kita mendapatkan perhitungan frame)
  3. tampilan

Yang tidak sepenuhnya jelas bagi saya adalah perbedaan batin antara -setNeedsLayoutdan -setNeedsUpdateConstraints. Dari Apple Documents:

setNeedsLayout

Panggil metode ini di utas utama aplikasi Anda ketika Anda ingin menyesuaikan tata letak subview tampilan. Metode ini mencatat permintaan dan segera kembali. Karena metode ini tidak memaksa pembaruan langsung, tetapi menunggu siklus pembaruan berikutnya, Anda dapat menggunakannya untuk membatalkan tata letak beberapa tampilan sebelum salah satu dari pandangan tersebut diperbarui. Perilaku ini memungkinkan Anda untuk menggabungkan semua pembaruan tata letak Anda ke satu siklus pembaruan, yang biasanya lebih baik untuk kinerja.

setNeedsUpdateConstraints

Ketika properti tampilan kustom Anda berubah dengan cara yang akan memengaruhi kendala, Anda dapat memanggil metode ini untuk menunjukkan bahwa kendala perlu diperbarui di beberapa titik di masa mendatang. Sistem kemudian akan memanggil updateConstraints sebagai bagian dari tata letak yang normal. Memperbarui kendala sekaligus sebelum mereka diperlukan memastikan bahwa Anda tidak perlu menghitung ulang kendala saat banyak perubahan dilakukan untuk tampilan Anda di antara lintasan tata letak.

Ketika saya ingin menghidupkan tampilan setelah memodifikasi batasan dan menghidupkan perubahan yang biasanya saya sebut misalnya:

[UIView animateWithDuration:1.0f delay:0.0f usingSpringWithDamping:0.5f initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        [self.modifConstrView setNeedsUpdateConstraints];
        [self.modifConstrView layoutIfNeeded];
    } completion:NULL];

Saya mengetahui bahwa jika saya menggunakan -setNeedsLayoutalih-alih -setNeedsUpdateConstraintssemuanya berfungsi seperti yang diharapkan, tetapi jika saya ganti -layoutIfNeededdengan -updateConstraintsIfNeeded, animasi tidak akan terjadi.
Saya sudah mencoba membuat kesimpulan sendiri:

  • -updateConstraintsIfNeeded hanya memperbarui kendala tetapi tidak memaksa tata letak untuk masuk ke proses, sehingga bingkai asli masih dipertahankan
  • -setNeedsLayoutpanggilan juga -updateContraintsmetode

Jadi kapan boleh menggunakan salah satu dari yang lain? dan tentang metode tata letak, apakah saya perlu memanggil mereka pada tampilan yang memiliki perubahan pada kendala atau pada tampilan induk?

Andrea
sumber
27
Saya tidak mengerti orang downvoting ... sungguh. JADI, Anda harus melakukan sesuatu tentang hal itu, seperti menanyakan alasan sebagai sesuatu yang wajib atau mereka sama sekali tidak ada gunanya
Andrea
7
Mungkin mereka hanya perlu mendapatkan lencana Kritik (suara turun pertama)
fujianjin6471
1
Saya sangat menyarankan Anda untuk melihat di sini . Jawabannya lebih dari solusi untuk masalah nyata. Lihat juga video ini
Sayang

Jawaban:

258

Kesimpulan Anda benar. Skema dasarnya adalah:

  • setNeedsUpdateConstraintsmemastikan panggilan selanjutnya untuk updateConstraintsIfNeededpanggilan updateConstraints.
  • setNeedsLayoutmemastikan panggilan selanjutnya untuk layoutIfNeededpanggilan layoutSubviews.

Ketika layoutSubviewsdipanggil, ia juga memanggil updateConstraintsIfNeeded, jadi menelepon secara manual jarang diperlukan dalam pengalaman saya. Bahkan, saya belum pernah menyebutnya kecuali saat men-debug layout.

Memperbarui batasan menggunakan setNeedsUpdateConstraintsjuga sangat jarang, objc.io – harus membaca tentang autolayout – mengatakan :

Jika ada perubahan nanti yang membatalkan salah satu kendala Anda, Anda harus segera menghapus kendala dan memanggil setNeedsUpdateConstraints. Bahkan, itulah satu-satunya kasus di mana Anda harus memicu pembaruan pass kendala.

Selain itu, dalam pengalaman saya, saya tidak pernah harus membatalkan batasan, dan tidak mengatur setNeedsLayoutbaris kode berikutnya, karena kendala baru cukup banyak meminta tata letak baru.

Aturan praktisnya adalah:

  • Jika Anda memanipulasi kendala secara langsung, hubungi setNeedsLayout.
  • Jika Anda mengubah beberapa kondisi (seperti offset atau pertiga) yang akan mengubah kendala dalam updateConstraintsmetode yang diganti (cara yang disarankan untuk mengubah kendala, btw), panggil setNeedsUpdateConstraints, dan sebagian besar waktu, setNeedsLayoutsetelah itu.
  • Jika Anda memerlukan salah satu tindakan di atas untuk memiliki efek langsung — misalnya ketika Anda perlu mempelajari ketinggian bingkai baru setelah tata letak berlalu — tambahkan dengan a layoutIfNeeded.

Juga, dalam kode animasi Anda, saya percaya setNeedsUpdateConstraintstidak diperlukan, karena kendala diperbarui sebelum animasi secara manual, dan animasi hanya menampilkan ulang tampilan berdasarkan perbedaan antara yang lama dan yang baru.

coverback
sumber
@coverback, so objc.io mengatakan, "Jika sesuatu berubah nanti yang membatalkan salah satu kendala Anda, Anda harus segera menghapus kendala dan memanggil setNeedsUpdateConstraints. Bahkan, itulah satu-satunya kasus di mana Anda harus memicu izin pembaruan kendala." Dan kemudian di blok Animasi dikatakan bahwa ketika saya menghapus, menambah atau mengubah constraint.contant saya harus memanggil setNeedsLayout. Apa bedanya? Saya merasa sangat bodoh :(
pash3r
3
@ pash3r Perbedaan memperbarui konstan tidak memenuhi syarat sebagai "pembatalan". Validasi adalah ketika tidak lagi relevan sama sekali, seperti harus dilampirkan ke tampilan lain atau dihapus sepenuhnya. Konstan hanya akan menempatkan pandangan lebih dekat atau lebih jauh, atau mengubah ukurannya, sehingga perlu setNeedsLayout.
coverback
@coverback setNeedsLayoutmemastikan layoutSubviewsakan dipanggil dalam siklus pembaruan berikutnya, tetapi mungkin ini tidak ada hubungannya dengan layoutIfNeeded?
fujianjin6471
2
@coverback Jika Anda memanipulasi kendala secara langsung, layoutSubviewsakan dipanggil secara otomatis, tidak perlu meneleponsetNeedsLayout
fujianjin6471
Ya, memanipulasi properti kendala secara langsung akan memicu layoutSubviews, jadi tidak perlu melakukannya secara manual. Namun, Anda harus menelepon layoutIfNeededjika Anda ingin perubahan segera berlaku alih-alih siklus tata letak berikutnya
Charlie Martin
89

The jawaban dengan coverback cukup benar. Namun, saya ingin menambahkan beberapa detail tambahan.

Di bawah ini adalah diagram dari siklus UIView khas yang menjelaskan perilaku lain:

Daur Hidup UIView

  1. Saya mengetahui bahwa jika saya menggunakan -setNeedsLayoutalih-alih -setNeedsUpdateConstraintssemuanya berfungsi seperti yang diharapkan, tetapi jika saya ganti -layoutIfNeededdengan -updateConstraintsIfNeeded, animasi tidak akan terjadi.

updateConstraintsbiasanya tidak melakukan apa pun. Itu hanya menyelesaikan kendala yang tidak berlaku sampai layoutSubviewsdipanggil. Jadi animasi memang membutuhkan panggilan layoutSubviews.

  1. panggilan setNeedsLayout juga metode -updateContraints

Tidak, ini tidak perlu. Jika kendala Anda belum dimodifikasi, UIView akan mengabaikan panggilan updateConstraints. Anda perlu secara eksplisit menelepon setNeedsUpdateConstraintuntuk memodifikasi kendala dalam proses.

Untuk menelepon, updateConstraintsAnda perlu melakukan yang berikut:

[view setNeedsUpdateConstraints];
[view setNeedsLayout]; 
[view layoutIfNeeded];
Kunal Balani
sumber
Terima kasih, ini menyelesaikan masalah saya. Saya punya UIWindow tanpa orangtua UIView yang memiliki kendala sementara ditambahkan padanya saat memanggil LayoutIfNeeded () sebelum animasi. Menambahkan pembungkus subview ke UIWindow dan memanggil ketiga metode ini untuk menyelesaikan masalah saya.
masterwok
Saya tidak berpikir bahwa memanggil layoutIfNeeded tepat setelah setNeedsLayout benar. Karena metode melakukan hal yang sama meskipun fakta bahwa satu menyebabkan tata letak untuk segera digambar ulang dan yang kedua dalam siklus pembaruan berikutnya.
fillky