Membuat animasi itu luar biasa. Mereka adalah bagian penting dari
Pedoman Antarmuka Manusia iOS . Animasi membantu menarik perhatian pengguna pada hal-hal penting atau hanya membuat aplikasi kurang membosankan.
Ada beberapa cara untuk menerapkan animasi di iOS. Mungkin cara yang paling populer adalah dengan menggunakan
UIView.animate (withDuration: animations :) . Anda dapat menghidupkan lapisan gambar menggunakan
CABasicAnimation . Selain itu, UIKit memungkinkan Anda untuk mengonfigurasi animasi khusus untuk menampilkan pengontrol menggunakan
UIViewControllerTransitioningDelegate .
Pada artikel ini saya ingin membahas cara lain yang menarik untuk menghidupkan tampilan -
UIViewPropertyAnimator . Kelas ini menyediakan lebih banyak fungsi manajemen daripada pendahulunya,
UIView.animat e. Gunakan untuk membuat animasi sementara, interaktif, dan terganggu. Selain itu, dimungkinkan untuk dengan cepat mengubah animator.
Memperkenalkan UIViewPropertyAnimator
UIViewPropertyAnimator diperkenalkan di
iOS 10 . Ini memungkinkan Anda untuk membuat animasi dengan cara berorientasi objek. Mari kita lihat contoh animasi yang dibuat menggunakan
UIViewPropertyAnimator .

Ini adalah bagaimana ketika menggunakan UIView.
UIView.animate(withDuration: 0.3) { view.frame = view.frame.offsetBy(dx: 100, dy: 0) }
Dan inilah cara melakukannya dengan
UIViewPropertyAnimator :
let animator = UIViewPropertyAnimator(duration:0.3, curve: .linear) { view.frame = view.frame.offsetBy(dx:100, dy:0) } animator.startAnimation()
Jika Anda perlu memeriksa animasi, cukup buat Playground dan jalankan kode seperti yang ditunjukkan di bawah ini. Kedua fragmen kode akan menghasilkan hasil yang sama.

Anda mungkin berpikir bahwa dalam contoh ini tidak ada banyak perbedaan. Jadi, apa gunanya menambahkan cara baru untuk membuat animasi?
UIViewPropertyAnimator menjadi lebih berguna ketika Anda perlu membuat animasi interaktif.
Animasi interaktif dan terputus
Apakah Anda ingat gerakan klasik "Finger Swipe to Unlock Device"? Atau gerakan "Gerakkan jari Anda di layar dari bawah ke atas" untuk membuka Pusat Kontrol? Ini adalah contoh bagus dari animasi interaktif. Anda dapat mulai memindahkan gambar dengan jari Anda, lalu lepaskan dan gambar akan kembali ke posisi semula. Selain itu, Anda dapat menangkap gambar selama animasi dan terus memindahkannya dengan jari Anda.
Animasi UIView tidak menyediakan cara mudah untuk mengontrol persentase penyelesaian animasi. Anda tidak dapat menjeda animasi di tengah-tengah loop dan melanjutkan eksekusi setelah terputus.
Dalam hal ini, kita akan berbicara tentang
UIViewPropertyAnimator . Selanjutnya, kita akan melihat bagaimana Anda dapat dengan mudah membuat animasi yang sepenuhnya interaktif, terputus, dan membalikkan animasi dalam beberapa langkah.
Persiapan proyek peluncuran
Pertama, Anda perlu
mengunduh proyek pemula .
Setelah Anda membuka arsip, Anda akan menemukan aplikasi
CityGuide yang membantu pengguna merencanakan liburan mereka. Pengguna dapat menggulir daftar kota, dan kemudian membuka deskripsi rinci dengan informasi rinci tentang kota yang ia sukai.
Pertimbangkan kode sumber proyek sebelum kita mulai membuat animasi yang indah. Inilah yang dapat Anda temukan di sebuah proyek dengan membukanya di
Xcode :
- ViewController.swift : Pengontrol aplikasi utama dengan UICollectionView yang menampilkan berbagai objek Kota .
- CityCollectionViewCell.swift: Cell untuk menampilkan City . Bahkan, dalam artikel ini sebagian besar perubahan akan berlaku untuk kelas ini. Anda mungkin memperhatikan bahwa descriptionLabel dan closeButton sudah didefinisikan di kelas. Namun, setelah memulai aplikasi, objek-objek ini akan disembunyikan. Jangan khawatir, mereka akan terlihat sedikit kemudian. Kelas ini juga memiliki properti collectionView dan index . Nanti mereka akan digunakan untuk animasi.
- CityCollectionViewFlowLayout.swift: Kelas ini bertanggung jawab untuk pengguliran horizontal. Kami belum akan mengubahnya.
- City.swift : Model aplikasi utama memiliki metode yang digunakan di ViewController.
- Main.storyboard: Di sana Anda dapat menemukan antarmuka pengguna untuk ViewController dan CityCollectionViewCell .
Mari kita coba membangun dan menjalankan aplikasi sampel. Sebagai hasil dari ini, kami memperoleh yang berikut ini.
Implementasi penyebaran dan runtuhnya animasi
Setelah memulai aplikasi, daftar kota ditampilkan. Tetapi pengguna tidak dapat berinteraksi dengan objek dalam bentuk sel. Sekarang Anda perlu menampilkan informasi untuk setiap kota ketika pengguna mengklik salah satu sel. Lihatlah versi final aplikasi. Inilah yang sebenarnya perlu dikembangkan:
Animasi terlihat bagus, bukan? Tapi tidak ada yang istimewa di sini, itu hanya logika dasar dari
UIViewPropertyAnimator . Mari kita lihat bagaimana menerapkan jenis animasi ini. Buat metode
collectionView (_: didSelectItemAt) , tambahkan fragmen kode berikut ke akhir file
ViewController :
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let selectedCell = collectionView.cellForItem(at: indexPath)! as! CityCollectionViewCell selectedCell.toggle() }
Sekarang kita perlu mengimplementasikan metode
toggle . Mari kita beralih ke
CityCollectionViewCell.swift dan mengimplementasikan metode ini.
Pertama, tambahkan enumerasi
Negara ke bagian atas file, tepat sebelum deklarasi kelas
CityCollectionViewCell . Daftar ini memungkinkan Anda untuk melacak keadaan sel:
private enum State { case expanded case collapsed var change: State { switch self { case .expanded: return .collapsed case .collapsed: return .expanded } } }
Tambahkan beberapa properti untuk mengontrol animasi di kelas
CityCollectionViewCell :
private var initialFrame: CGRect? private var state: State = .collapsed private lazy var animator: UIViewPropertyAnimator = { return UIViewPropertyAnimator(duration: 0.3, curve: .easeInOut) }()
Variabel
initialFrame digunakan untuk menyimpan bingkai sel sampai animasi
berjalan .
negara digunakan untuk melacak jika sel diperluas atau runtuh. Dan variabel
animator digunakan untuk mengontrol animasi.
Sekarang tambahkan metode
sakelar dan panggil dari metode
tutup , misalnya:
@IBAction func close(_ sender: Any) { toggle() } func toggle() { switch state { case .expanded: collapse() case .collapsed: expand() } }
Lalu kami menambahkan dua metode lagi:
expand () dan
collapse () . Kami akan melanjutkan implementasi mereka. Pertama, kita mulai dengan metode
expansiond () :
private func expand() { guard let collectionView = self.collectionView, let index = self.index else { return } animator.addAnimations { self.initialFrame = self.frame self.descriptionLabel.alpha = 1 self.closeButton.alpha = 1 self.layer.cornerRadius = 0 self.frame = CGRect(x: collectionView.contentOffset.x, y:0, width: collectionView.frame.width, height: collectionView.frame.height) if let leftCell = collectionView.cellForItem(at: IndexPath(row: index - 1, section: 0)) { leftCell.center.x -= 50 } if let rightCell = collectionView.cellForItem(at: IndexPath(row: index + 1, section: 0)) { rightCell.center.x += 50 } self.layoutIfNeeded() } animator.addCompletion { position in switch position { case .end: self.state = self.state.change collectionView.isScrollEnabled = false collectionView.allowsSelection = false default: () } } animator.startAnimation() }
Berapa kodenya. Biarkan saya menjelaskan apa yang terjadi langkah demi langkah:
- Pertama, periksa apakah collectionView dan index tidak sama dengan nol. Jika tidak, kami tidak akan dapat memulai animasi.
- Selanjutnya, mulai membuat animasi dengan memanggil animator.addAnimations .
- Selanjutnya, simpan frame saat ini, yang digunakan untuk mengembalikannya dalam animasi konvolusi.
- Kemudian kita mengatur nilai alpha untuk descriptionLabel dan closeButton untuk membuatnya terlihat.
- Selanjutnya, hapus sudut bulat dan atur bingkai baru untuk sel. Sel akan ditampilkan di layar penuh.
- Selanjutnya kita pindahkan sel tetangga.
- Sekarang panggil metode animator.addComplete () untuk menonaktifkan interaksi gambar koleksi. Ini mencegah pengguna untuk menggulirnya selama ekspansi sel. Juga ubah status sel saat ini. Penting untuk mengubah keadaan sel, dan hanya setelah itu animasi berakhir.
Sekarang tambahkan animasi konvolusi. Singkatnya, hanya saja kita mengembalikan sel ke keadaan sebelumnya:
private func collapse() { guard let collectionView = self.collectionView, let index = self.index else { return } animator.addAnimations { self.descriptionLabel.alpha = 0 self.closeButton.alpha = 0 self.layer.cornerRadius = self.cornerRadius self.frame = self.initialFrame! if let leftCell = collectionView.cellForItem(at: IndexPath(row: index - 1, section: 0)) { leftCell.center.x += 50 } if let rightCell = collectionView.cellForItem(at: IndexPath(row: index + 1, section: 0)) { rightCell.center.x -= 50 } self.layoutIfNeeded() } animator.addCompletion { position in switch position { case .end: self.state = self.state.change collectionView.isScrollEnabled = true collectionView.allowsSelection = true default: () } } animator.startAnimation() }
Sekarang saatnya mengkompilasi dan menjalankan aplikasi. Coba klik pada sel dan Anda akan melihat animasinya. Untuk menutup gambar, klik ikon silang di sudut kanan atas.
Menambahkan pemrosesan gerakan
Anda dapat mengklaim mencapai hasil yang sama menggunakan
UIView.animate . Apa gunanya menggunakan
UIViewPropertyAnimator ?
Nah, ini saatnya membuat animasi menjadi interaktif. Tambahkan
UIPanGestureRecognizer dan properti baru bernama
popupOffset untuk melacak seberapa banyak sel dapat dipindahkan. Mari kita deklarasikan variabel-variabel ini di kelas
CityCollectionViewCell :
private let popupOffset: CGFloat = (UIScreen.main.bounds.height - cellSize.height)/2.0 private lazy var panRecognizer: UIPanGestureRecognizer = { let recognizer = UIPanGestureRecognizer() recognizer.addTarget(self, action: #selector(popupViewPanned(recognizer:))) return recognizer }()
Kemudian tambahkan metode berikut untuk mendaftarkan definisi gesek:
override func awakeFromNib() { self.addGestureRecognizer(panRecognizer) }
Sekarang Anda perlu menambahkan metode
popupViewPanned untuk melacak gerakan gesek. Rekatkan kode berikut ke
CityCollectionViewCell :
@objc func popupViewPanned(recognizer: UIPanGestureRecognizer) { switch recognizer.state { case .began: toggle() animator.pauseAnimation() case .changed: let translation = recognizer.translation(in: collectionView) var fraction = -translation.y / popupOffset if state == .expanded { fraction *= -1 } animator.fractionComplete = fraction case .ended: animator.continueAnimation(withTimingParameters: nil, durationFactor: 0) default: () } }
Ada tiga negara. Di awal gerakan, kami menginisialisasi animator menggunakan metode
sakelar dan segera menghentikannya. Sementara pengguna menyeret sel, kami memperbarui animasi dengan mengatur properti pengali
fractionComplete . Ini adalah keajaiban utama animator yang memungkinkan mereka untuk mengontrol. Akhirnya, ketika pengguna melepaskan jarinya, metode animator
continueAnimation dipanggil untuk melanjutkan animasi. Kemudian sel akan bergerak ke posisi target.
Setelah memulai aplikasi, Anda dapat menarik sel ke atas untuk mengembangkannya. Lalu seret sel yang diperluas ke bawah untuk menutupnya.
Animasi sekarang terlihat cukup bagus, tetapi mengganggu animasi di tengah tidak mungkin. Karena itu, untuk membuat animasi sepenuhnya interaktif, Anda perlu menambahkan fungsi lain - interupsi. Pengguna dapat memulai memperluas / runtuh animasi seperti biasa, tetapi animasi harus dihentikan segera setelah pengguna mengklik sel selama siklus animasi.
Untuk melakukan ini, simpan kemajuan animasi dan kemudian ambil nilai ini untuk menghitung persentase penyelesaian animasi.
Pertama, nyatakan properti baru di
CityCollectionViewCell :
private var animationProgress: CGFloat = 0
Kemudian perbarui blok
.began metode
popupViewPanned dengan baris kode berikut untuk mengingat progresnya:
animationProgress = animator.fractionComplete
Di blok
.changed ,
Anda perlu memperbarui baris kode berikut untuk menghitung persentase penyelesaian dengan benar:
animator.fractionComplete = fraction + animationProgress
Sekarang aplikasi siap untuk diuji. Jalankan proyek dan lihat apa yang terjadi. Jika semua tindakan dilakukan dengan benar mengikuti instruksi saya, animasi akan terlihat seperti ini:
Animasi terbalik
Anda dapat menemukan cacat untuk implementasi saat ini. Jika Anda menarik sel sedikit dan mengembalikannya ke posisi semula, sel akan terus membesar saat Anda melepaskan jari Anda. Mari kita selesaikan masalah ini untuk membuat animasi interaktif lebih baik.
Mari perbarui blok
.end dari metode
popupViewPanned , seperti yang dijelaskan di bawah ini:
let velocity = recognizer.velocity(in: self) let shouldComplete = velocity.y > 0 if velocity.y == 0 { animator.continueAnimation(withTimingParameters: nil, durationFactor: 0) break } switch state { case .expanded: if !shouldComplete && !animator.isReversed { animator.isReversed = !animator.isReversed } if shouldComplete && animator.isReversed { animator.isReversed = !animator.isReversed } case .collapsed: if shouldComplete && !animator.isReversed { animator.isReversed = !animator.isReversed } if !shouldComplete && animator.isReversed { animator.isReversed = !animator.isReversed } } animator.continueAnimation(withTimingParameters: nil, durationFactor: 0)
Sekarang kita memperhitungkan kecepatan gerakan untuk menentukan apakah animasi harus dibalik.
Dan akhirnya, masukkan baris kode lain ke dalam blok yang
diubah . Letakkan kode ini di sebelah kanan perhitungan
animator.fractionComplete .
if animator.isReversed { fraction *= -1 }
Ayo jalankan aplikasi lagi. Sekarang semuanya harus bekerja tanpa gagal.
Perbaiki gerakan pan
Jadi, kami telah menyelesaikan implementasi animasi menggunakan
UIViewPropertyAnimator . Namun, ada satu kesalahan yang tidak menyenangkan. Anda mungkin telah bertemu dengannya saat menguji aplikasi. Masalahnya adalah bahwa menggulir sel secara horizontal tidak mungkin. Mari kita coba geser ke kiri / kanan melalui sel, dan kita dihadapkan dengan masalah.
Alasan utama terkait dengan
UIPanGestureRecognizer yang kami
buat . Itu juga menangkap gerakan swipe, dan bertentangan dengan
UICollectionViewView gesture built-in.
Meskipun pengguna masih dapat menggulir melalui bagian atas / bawah sel atau ruang antar sel untuk menggulir kota, saya masih tidak menyukai antarmuka pengguna yang buruk. Mari kita perbaiki.
Untuk menyelesaikan konflik, kita perlu mengimplementasikan metode delegasi yang disebut
gestRecognizerShouldBegin (_ :) . Metode ini mengontrol apakah pengenal isyarat harus terus menginterpretasikan sentuhan. Jika Anda mengembalikan
false dalam metode ini, pengenal isyarat akan mengabaikan sentuhan. Jadi yang akan kita lakukan adalah memberikan alat pengenalan panorama kita sendiri kemampuan untuk mengabaikan gerakan horisontal.
Untuk melakukan ini, mari atur
delegasi pengenal pan kami. Masukkan baris kode berikut ke inisialisasi
panRecognizer (Anda dapat meletakkan kode tepat sebelum
pengenal balik :
recognizer.delegate = self
Kemudian, kami menerapkan metode
gestRecognizerShouldBegin (_ :) sebagai berikut:
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return abs((panRecognizer.velocity(in: panRecognizer.view)).y) > abs((panRecognizer.velocity(in: panRecognizer.view)).x) }
Kami akan membuka / menutup jika kecepatan vertikal lebih besar dari kecepatan horizontal.
Wow! Mari kita coba aplikasi ini lagi. Sekarang Anda dapat bergerak di sekitar daftar kota dengan menggesekkan kiri / kanan melintasi sel.
Bonus: Fitur Sinkronisasi Kustom
Sebelum kita menyelesaikan tutorial ini, mari kita bicara tentang fungsi pengaturan waktu. Apakah Anda masih ingat kasus ketika pengembang meminta Anda untuk menerapkan fungsi sinkronisasi khusus untuk animasi yang Anda buat?
Biasanya, Anda harus mengubah
UIView.animation menjadi
CABasicAnimation atau membungkusnya dengan
CATransaction .
Dengan UIViewPropertyAnimator, Anda dapat dengan mudah mengimplementasikan fungsi pengaturan waktu kustom.
Fungsi pengaturan waktu (atau fungsi pelonggaran) dipahami sebagai fungsi kecepatan animasi yang memengaruhi laju perubahan satu atau properti animasi lainnya. Empat jenis saat ini didukung: easyInOut, easyIn, easyOut, linear.
Ganti inisialisasi animator dengan fungsi pengaturan waktu ini (coba gambar kurva kubik Bezier Anda sendiri) sebagai berikut:
private lazy var animator: UIViewPropertyAnimator = { let cubicTiming = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.17, y: 0.67), controlPoint2: CGPoint(x: 0.76, y: 1.0)) return UIViewPropertyAnimator(duration: 0.3, timingParameters: cubicTiming) }()
Atau, alih-alih menggunakan parameter sinkronisasi kubik, Anda juga dapat menggunakan sinkronisasi pegas, misalnya:
let springTiming = UISpringTimingParameters(mass: 1.0, stiffness: 2.0, damping: 0.2, initialVelocity: .zero)
Cobalah untuk memulai proyek lagi dan lihat apa yang terjadi.
Kesimpulan
Melalui UIViewPropertyAnimator, Anda dapat meningkatkan layar statis dan interaksi pengguna melalui animasi interaktif.
Saya tahu bahwa Anda tidak sabar untuk menyadari apa yang telah Anda pelajari dalam proyek Anda sendiri. Jika Anda menerapkan pendekatan ini dalam proyek Anda, itu akan sangat keren, beri tahu saya tentang ini dengan meninggalkan komentar di bawah.
Sebagai referensi, di sini Anda dapat
mengunduh draft terakhir .
Tautan selanjutnya
Animasi profesional menggunakan UIKit -
https://developer.apple.com/videos/play/wwdc2017/230/Dokumentasi UIViewPropertyAnimator untuk Pengembang Apple -
https://developer.apple.com/documentation/uikit/uiviewpropertyanimator