Desain atom dan desain sistem sangat populer dalam desain: inilah saat semuanya terdiri dari komponen, dari kontrol hingga layar. Tidaklah sulit bagi seorang programmer untuk menulis kontrol terpisah, tetapi apa yang harus dilakukan dengan seluruh layar?
Mari kita lihat contoh Tahun Baru:
- mari kita menyatukan semuanya;
- dibagi menjadi pengontrol: pilih navigasi, templat dan konten;
- gunakan kembali kode untuk layar lain.

Semua dalam banyak
Layar Tahun Baru ini berbicara tentang jam buka khusus restoran pizza. Ini cukup sederhana, jadi tidak akan menjadi kejahatan untuk menjadikannya satu pengendali:

Tapi Lain kali, ketika kita membutuhkan layar yang sama, kita harus mengulang semuanya lagi, dan kemudian membuat perubahan yang sama untuk semua layar. Ya, itu tidak terjadi tanpa suntingan.
Karena itu, lebih masuk akal untuk membaginya menjadi beberapa bagian dan menggunakannya untuk layar lain. Saya menyoroti tiga:
- navigasi
- templat dengan area untuk konten dan tempat untuk tindakan di bagian bawah layar,
- konten unik di tengah.
Pilih setiap bagian dalam UIViewController
sendiri.
Navigasi kontainer
Contoh wadah navigasi yang paling mencolok adalah UINavigationController
dan UITabBarController
. Masing-masing menempati strip di layar di bawah kontrolnya sendiri, dan meninggalkan ruang yang tersisa untuk UIViewController
lain.
Dalam kasus kami, akan ada wadah untuk semua layar modal dengan hanya satu tombol tutup.
Apa gunanyaJika kita ingin memindahkan tombol ke kanan, maka kita hanya perlu mengubahnya dalam satu pengontrol.
Atau, jika kami memutuskan untuk menampilkan semua modal windows dengan animasi khusus, dan menutup secara interaktif dengan sapuan, seperti pada kartu cerita AppStore. Maka UIViewControllerTransitioningDelegate
perlu diatur hanya untuk pengontrol ini.

Anda dapat menggunakan tampilan container view
untuk memisahkan pengontrol: itu akan membuat UIView
di induk dan memasukkan UIView
pengontrol anak di dalamnya.

Regangkan container view
ke tepi layar. Safe area
akan secara otomatis berlaku untuk pengendali anak:

Pola layar
Kontennya jelas di layar: gambar, judul, teks. Tombolnya tampaknya menjadi bagian dari itu, tetapi kontennya dinamis pada iPhone yang berbeda, dan tombolnya sudah diperbaiki. Dua sistem dengan tugas yang berbeda terlihat: satu menampilkan konten, dan yang lainnya menyematkan dan menyelaraskannya. Mereka harus dibagi menjadi dua pengendali.

Yang pertama bertanggung jawab untuk tata letak layar: konten harus di tengah, dan tombol dipaku ke bagian bawah layar. Yang kedua akan menggambar konten.

Tanpa templat, semua pengontrol sama, tetapi elemen menari.
Tombol-tombol pada layar terakhir berbeda - itu tergantung pada konten. Delegasi akan membantu menyelesaikan masalah: controller-template akan meminta kontrol dari konten dan menampilkannya dalam UIStackView
- UIStackView
.
Tombol dapat dilampirkan ke pengontrol melalui objek terkait. IBOutlet
dan IBAction
mereka disimpan dalam pengontrol konten, hanya saja elemen tidak ditambahkan ke hierarki.

Anda bisa mendapatkan elemen dari konten dan menambahkannya ke templat pada tahap persiapan UIStoryboardSegue
:
Di setter, kami menambahkan kontrol ke UIStackView
:
Akibatnya, pengontrol kami dibagi menjadi tiga bagian: navigasi, templat, dan konten. Dalam gambar, semua container view
ditampilkan dalam warna abu-abu:

Ukuran pengontrol dinamis
Pengontrol konten memiliki ukuran maksimumnya sendiri, dibatasi oleh constraints
internal.
Container view
menambahkan constores berdasarkan Autoresizing mask
, dan mereka bertentangan dengan dimensi internal konten. Masalahnya dipecahkan dalam kode: di pengontrol konten, Anda perlu menunjukkan bahwa itu tidak terpengaruh oleh konstores dari Autoresizing mask
:

Ada dua langkah lagi untuk Interface Builder:
Langkah 1. Tentukan Intrinsic size
untuk UIView
. Nilai riil akan muncul setelah peluncuran, tetapi untuk saat ini kami akan memasukkan yang sesuai.

Langkah 2. Untuk pengontrol konten, tentukan Simulated Size
. Mungkin tidak cocok dengan ukuran sebelumnya.
Ada kesalahan tata letak, apa yang harus saya lakukan?Kesalahan terjadi ketika AutoLayout
tidak dapat menemukan cara untuk menguraikan elemen dalam ukuran saat ini.
Paling sering, masalah hilang setelah mengubah prioritas konstanta. Anda harus meletakkannya sehingga salah satu UIView
dapat memperluas / membuat kontrak lebih dari yang lain.
Kami membagi menjadi beberapa bagian dan menulis dalam kode
Kami membagi pengontrol menjadi beberapa bagian, tetapi sejauh ini kami tidak dapat menggunakannya kembali, antarmuka dari UIStoryboard
sulit untuk diekstraksi menjadi beberapa bagian. Jika kita perlu mentransfer beberapa data ke konten, maka kita harus mengetuknya melalui seluruh hierarki. Itu harus sebaliknya: pertama ambil konten, konfigurasikan, lalu bungkus dalam wadah yang diperlukan. Seperti bola lampu.
Tiga tugas muncul di jalan kami:
- Pisahkan setiap pengontrol ke dalam
UIStoryboard
sendiri. - Menolak
container view
, tambahkan pengontrol ke wadah dalam kode. - Ikat semuanya kembali.
Berbagi UIStoryboard
Anda perlu membuat dua UIStoryboard
tambahan dan menyalin-paste pengontrol navigasi dan pengontrol template ke dalamnya. Embed segue
akan pecah, tetapi tampilan container view
dengan batasan yang dikonfigurasi akan ditransfer. Kendala harus diselamatkan, dan tampilan container view
harus diganti dengan UIView
biasa.
Cara termudah adalah mengubah jenis tampilan Kontainer dalam kode UIStoryboard.- buka
UIStoryboard
sebagai kode (menu konteks file → Buka sebagai ... → Kode sumber); ubah jenisnya dari containerView
untuk view
. Penting untuk mengubah tag pembuka dan penutup .
Dengan cara yang sama, Anda dapat mengubah, misalnya, UIView
ke UIScrollView
, jika perlu. Begitu juga sebaliknya.

Kami mengatur pengontrol ke properti is initial view controller
, dan UIStoryboard
akan memanggil UIStoryboard
sebagai pengontrol.
Kami memuat pengontrol dari UIStoryboard.Jika nama pengontrol cocok dengan nama UIStoryboard
, maka unduhan dapat dibungkus dengan metode yang dengan sendirinya akan menemukan file yang diinginkan:
protocol Storyboardable { } extension Storyboardable where Self: UIViewController { static func instantiateInitialFromStoryboard() -> Self { let controller = storyboard().instantiateInitialViewController() return controller! as! Self } static func storyboard(fileName: String? = nil) -> UIStoryboard { let storyboard = UIStoryboard(name: fileName ?? storyboardIdentifier, bundle: nil) return storyboard } static var storyboardIdentifier: String { return String(describing: self) } static var storyboardName: String { return storyboardIdentifier } }
Jika controller dijelaskan dalam .xib
, maka konstruktor standar akan memuat tanpa tarian tersebut. Sayangnya, .xib
hanya dapat berisi satu controller, seringkali ini tidak cukup: dalam kasus yang baik, satu layar terdiri dari beberapa. Oleh karena itu, kami menggunakan UIStoryborad
, mudah untuk memecah layar menjadi beberapa bagian.
Tambahkan pengontrol dalam kode
Agar controller berfungsi dengan baik, kita membutuhkan semua metode siklus hidupnya: will/did-appear/disappear
.
Untuk tampilan yang benar, Anda perlu menghubungi 5 langkah:
willMove(toParent parent: UIViewController?) addChild(_ childController: UIViewController) addSubview(_ subivew: UIView) layout didMove(toParent parent: UIViewController?)
Apple menyarankan untuk mengurangi kode menjadi 4 langkah, karena addChild()
sendiri akan memanggil willMove(toParent)
. Singkatnya:
addChild(_ childController: UIViewController) addSubview(_ subivew: UIView) layout didMove(toParent parent: UIViewController?)
Untuk kesederhanaan, Anda bisa membungkus semuanya dalam extension
. Untuk kasus kami, kami memerlukan versi dengan insertSubview()
.
extension UIViewController { func insertFullframeChildController(_ childController: UIViewController, toView: UIView? = nil, index: Int) { let containerView: UIView = toView ?? view addChild(childController) containerView.insertSubview(childController.view, at: index) containerView.pinToBounds(childController.view) childController.didMove(toParent: self) } }
Untuk menghapus, Anda memerlukan langkah-langkah yang sama, hanya alih-alih pengontrol induk, Anda perlu mengatur nil
. Sekarang removeFromParent()
memanggil didMove(toParent: nil)
, dan tata letak tidak diperlukan. Versi singkatnya sangat berbeda:
willMove(toParent: nil) view.removeFromSuperview() removeFromParent()
Tata letak
Tetapkan Kendala
Untuk mengatur ukuran controller dengan benar, kita akan menggunakan AutoLayout
. Kita perlu memaku semua sisi ke semua sisi:
extension UIView { func pinToBounds(_ view: UIView) { view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ view.topAnchor.constraint(equalTo: topAnchor), view.bottomAnchor.constraint(equalTo: bottomAnchor), view.leadingAnchor.constraint(equalTo: leadingAnchor), view.trailingAnchor.constraint(equalTo: trailingAnchor) ]) } }
Tambahkan pengontrol anak dalam kode
Sekarang semuanya bisa digabungkan:
Karena frekuensi penggunaan, kami dapat membungkus semua ini dalam extension
:
Metode serupa juga diperlukan untuk pengontrol templat. prepare(for segue:)
digunakan untuk menyiapkan dalam prepare(for segue:)
, tetapi sekarang Anda dapat mengikatnya dalam metode embed pengontrol:
Membuat pengontrol terlihat seperti ini:
Menghubungkan layar baru ke template itu mudah:
- hapus apa yang tidak relevan dengan konten;
- tentukan tombol tindakan dengan mengimplementasikan protokol OnboardingViewControllerDatasource;
- tulis metode yang menautkan templat dan konten.
Lebih lanjut tentang wadah
Bilah status
Seringkali diperlukan agar visibilitas status bar
dikontrol oleh pengontrol dengan konten, bukan wadah. Ada beberapa property
untuk ini:
Menggunakan property
ini property
Anda dapat membuat rantai pengontrol, yang terakhir akan bertanggung jawab untuk menampilkan status bar
.
Area aman
Jika tombol wadah tumpang tindih konten, maka Anda harus meningkatkan zona safeArea
. Ini dapat dilakukan dalam kode: set additinalSafeAreaInsets
untuk pengendali anak. Anda dapat memanggilnya dari embedController()
:
private func addSafeArea(to controller: UIViewController) { if #available(iOS 11.0, *) { let buttonHeight = CGFloat(30) let topInset = UIEdgeInsets(top: buttonHeight, left: 0, bottom: 0, right: 0) controller.additionalSafeAreaInsets = topInset } }
Jika Anda menambahkan 30 poin di atas, tombol akan berhenti konten yang tumpang tindih dan safeArea
akan menempati area hijau:

Margin. Pertahankan margin superview
Pengendali memiliki margins
standar. Biasanya mereka sama dengan 16 poin dari setiap sisi layar dan hanya pada ukuran Plus mereka adalah 20 poin.
Berdasarkan margins
Anda dapat membuat konstanta, lekukan ke tepi akan berbeda untuk iPhone yang berbeda:

Ketika kami menempatkan satu UIView
ke yang lain, margins
dibagi dua: menjadi 8 poin. Untuk mencegah hal ini, Anda harus menyertakan Preserve superview margins
. Maka margins
anak UIView
akan sama dengan margins
tua. Sangat cocok untuk wadah layar penuh.
Akhirnya
Pengontrol kontainer adalah alat yang ampuh. Mereka menyederhanakan kode, memisahkan tugas, dan dapat digunakan kembali. Anda dapat menulis pengontrol bersarang dengan cara apa pun: di UIStoryboard
, dalam .xib
atau hanya dalam kode. Yang paling penting, mereka mudah dibuat dan menyenangkan untuk digunakan.
→ Contoh dari artikel di GitHub
Apakah Anda memiliki layar yang layak dijadikan templat? Bagikan komentar Anda!