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.
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).
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.
Bermain-main dengan nilai-nilai dalam animasi dan pertimbangkan untuk menggunakan varian animateWithDuration lainnya.
Jangan hardcode nilai tata letak khusus sebagai kriteria untuk menentukan perubahan konstanta lain, alih-alih gunakan nilai yang memungkinkan Anda untuk menentukan lokasi tampilan. CGRectContainsRect
adalah salah satu contohnya
- Jika perlu, jangan ragu untuk menggunakan margin tata letak yang terkait dengan tampilan yang berpartisipasi dalam definisi kendala
let viewMargins = self.webview.layoutMarginsGuide
: misalnya
- Jangan lakukan pekerjaan yang tidak harus Anda lakukan, semua tampilan dengan kendala pada storyboard memiliki kendala yang melekat pada properti itu sendiri. ViewName.constraints
- 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)
- 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)
- 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.
- 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.