Saya ingin menggunakan tampilan di seluruh banyak viewcontrollers di storyboard. Jadi, saya berpikir tentang mendesain tampilan dalam xib eksternal sehingga perubahan tercermin di setiap viewcontroller. Tetapi bagaimana kita bisa memuat tampilan dari xib eksternal dalam storyboard dan apakah itu mungkin? Jika bukan itu masalahnya, apa alternatif lain yang tersedia untuk menyesuaikan situasi ini?
ios
uiview
storyboard
xib
Sebastian Hoffmann
sumber
sumber
Jawaban:
Contoh lengkap saya ada di sini , tetapi saya akan memberikan ringkasan di bawah ini.
Tata letak
Tambahkan file .swift dan .xib masing-masing dengan nama yang sama ke proyek Anda. File .xib berisi tata letak tampilan khusus Anda (lebih disukai menggunakan batasan tata letak otomatis).
Jadikan file cepat sebagai pemilik file xib.
Kode
Tambahkan kode berikut ke file .swift dan kaitkan outlet dan tindakan dari file .xib.
Gunakan
Gunakan tampilan khusus Anda di mana saja di storyboard Anda. Cukup tambahkan
UIView
dan atur nama kelas ke nama kelas khusus Anda.sumber
init(frame:)
. Lihat tutorial ini untuk lebih jelasnya.Untuk sementara pendekatan Christopher Swasey adalah pendekatan terbaik yang saya temukan. Saya bertanya kepada beberapa devs senior di tim saya tentang hal itu dan salah satu dari mereka memiliki solusi yang sempurna ! Ini memenuhi semua kekhawatiran yang ditangani oleh Christopher Swasey dengan fasih dan tidak memerlukan kode subkelas boilerplate (perhatian utama saya dengan pendekatannya). Ada satu gotcha , tetapi selain itu cukup intuitif dan mudah diterapkan.
MyCustomClass.swift
MyCustomClass.xib
File's Owner
file .xib menjadi kelas khusus Anda (MyCustomClass
)class
nilai (di bawahidentity Inspector
) untuk tampilan khusus Anda dalam file .xib kosong. Jadi tampilan khusus Anda tidak akan memiliki kelas yang ditentukan, tetapi akan memiliki Pemilik File yang ditentukan.Assistant Editor
.Connections Inspector
Anda akan melihat bahwa Outlet Referensi Anda tidak mereferensikan kelas kustom Anda (yaituMyCustomClass
), melainkan referensiFile's Owner
. KarenaFile's Owner
ditentukan sebagai kelas khusus Anda, outlet akan terhubung dan berfungsi sebagaimana mestinya.NibLoadable
protokol yang dirujuk di bawah ini..swift
nama file kelas kustom Anda berbeda dari.xib
nama file Anda , maka aturnibName
properti menjadi nama.xib
file Anda .required init?(coder aDecoder: NSCoder)
danoverride init(frame: CGRect)
panggilsetupFromNib()
seperti contoh di bawah ini.MyCustomClass
).Berikut adalah protokol yang ingin Anda rujuk:
Dan di sini adalah contoh dari
MyCustomClass
yang mengimplementasikan protokol (dengan nama file .xibMyCustomClass.xib
):CATATAN: Jika Anda melewatkan Gotcha dan menetapkan
class
nilai di dalam file .xib Anda menjadi kelas kustom Anda, maka itu tidak akan menggambar di storyboard dan Anda akan mendapatkanEXC_BAD_ACCESS
kesalahan ketika Anda menjalankan aplikasi karena macet dalam loop tak terbatas dari mencoba menginisialisasi kelas dari nib menggunakaninit?(coder aDecoder: NSCoder)
metode yang kemudian memanggilSelf.nib.instantiate
dan memanggilinit
lagi.sumber
setupFromNib()
, tampaknya memperbaiki masalah tata letak otomatis tertentu yang aneh dengan sel tampilan tabel ukuran otomatis yang berisi tampilan yang dibuat-XIB.@IBDesignable
kompatibilitas. Tidak dapat percaya mengapa Xcode atau UIKit tidak menyediakan sesuatu seperti ini sebagai default saat menambahkan file UIView.Dengan asumsi bahwa Anda telah membuat xib yang ingin Anda gunakan:
1) Buat subkelas khusus dari UIView (Anda dapat membuka File -> New -> File ... -> Cocoa Touch Class. Pastikan "Subkelas dari:" adalah "UIView").
2) Tambahkan tampilan yang didasarkan pada xib sebagai subview untuk tampilan ini saat inisialisasi.
Dalam Obj-C
Dalam Swift 2
Dalam Swift 3
3) Di mana pun Anda ingin menggunakannya di storyboard Anda, tambahkan UIView seperti biasanya, pilih tampilan yang baru ditambahkan, buka Identity Inspector (ikon ketiga di kanan atas yang terlihat seperti persegi panjang dengan garis-garis di dalamnya), dan masukkan nama subclass Anda sebagai "Kelas" di bawah "Kelas Kustom".
sumber
xibView.frame = self.frame;
seharusnyaxibView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
, jika tidak xibView akan memiliki offset ketika tampilan ditambahkan ke storyboard.Saya selalu menemukan solusi "tambahkan sebagai subview" tidak memuaskan, karena sekrup dengan (1) autolayout, (2)
@IBInspectable
, dan (3) outlet. Sebagai gantinya, izinkan saya memperkenalkan Anda pada keajaibanawakeAfter:
,NSObject
metode.awakeAfter
memungkinkan Anda menukar objek yang sebenarnya dibangunkan dari NIB / Storyboard dengan objek yang sama sekali berbeda. Itu objek kemudian dimasukkan melalui proses hidrasi, memilikiawakeFromNib
disebut di atasnya, ditambahkan sebagai pandangan, dllKita bisa menggunakan ini dalam subkelas "kardus cut-out" dari pandangan kita, satu-satunya tujuan yang akan memuat pandangan dari NIB dan mengembalikannya untuk digunakan dalam Storyboard. Subclass yang dapat disematkan kemudian ditentukan dalam inspektur identitas tampilan Storyboard, daripada kelas aslinya. Sebenarnya tidak harus menjadi subclass agar ini berfungsi, tetapi menjadikannya subclass adalah apa yang memungkinkan IB untuk melihat properti IBInspectable / IBOutlet.
Pelat ekstra ini mungkin tampak suboptimal — dan dalam arti tertentu, karena idealnya
UIStoryboard
akan menangani hal ini dengan mulus — tetapi memiliki keuntungan meninggalkan NIB asli danUIView
subkelas sepenuhnya tidak dimodifikasi. Peran yang dimainkannya pada dasarnya adalah kelas adaptor atau jembatan, dan sangat valid, desain-bijaksana, sebagai kelas tambahan, bahkan jika itu disesalkan. Di sisi lain, jika Anda lebih suka pelit dengan kelas Anda, solusi @ BenPatch bekerja dengan mengimplementasikan protokol dengan beberapa perubahan kecil lainnya. Pertanyaan tentang solusi mana yang lebih baik bermuara pada masalah gaya programmer: apakah orang lebih suka komposisi objek atau multiple inheritance.Catatan: kelas yang diatur pada tampilan di file NIB tetap sama. Subclass yang dapat disematkan hanya digunakan di storyboard. Subkelas tidak dapat digunakan untuk membuat tampilan dalam kode, jadi seharusnya tidak memiliki logika tambahan. Seharusnya hanya berisi
awakeAfter
kait.⚠️ Satu kelemahan signifikan di sini adalah jika Anda menentukan batasan rasio lebar, tinggi, atau aspek dalam storyboard yang tidak berhubungan dengan tampilan lain, maka harus disalin secara manual. Kendala yang berhubungan dengan dua pandangan dipasang pada leluhur bersama terdekat, dan pandangan terbangun dari storyboard dari dalam-luar, sehingga pada saat kendala terhidrasi pada superview pertukaran telah terjadi. Kendala yang hanya melibatkan tampilan yang dipermasalahkan dipasang langsung pada tampilan itu, dan dengan demikian dilemparkan ketika swap terjadi kecuali mereka disalin.
Perhatikan bahwa yang terjadi di sini adalah kendala yang dipasang pada tampilan di storyboard disalin ke tampilan yang baru dipakai , yang mungkin sudah memiliki kendala sendiri, yang didefinisikan dalam file nib-nya. Mereka tidak terpengaruh.
instantiateViewFromNib
adalah ekstensi aman untukUIView
. Yang dilakukannya hanyalah mengulang-ulang objek NIB hingga menemukan yang cocok dengan tipenya. Perhatikan bahwa tipe generik adalah nilai balik , jadi tipe tersebut harus ditentukan di situs panggilan.sumber
instantiateViewFromNib
tidak akan mengembalikan apa pun. Bukan masalah besar bagaimanapun IMO, subclass hanyalah alat untuk menghubungkan ke storyboard, semua kode harus pada kelas asli.MyCustomView
. Di xib saya bilah sisi dalam sebelah kiri tidak ada secara default; untuk menyalakannya, ada tombol di sebelah kontrol ciri "View as: iPhone 7" dekat sisi bawah / kiri.Solusi terbaik saat ini adalah dengan hanya menggunakan pengontrol tampilan kustom dengan tampilan yang ditentukan dalam xib, dan cukup hapus properti "view" yang dibuat Xcode di dalam storyboard saat menambahkan pengontrol tampilan ke dalamnya (jangan lupa untuk mengatur nama kelas kustom).
Ini akan membuat runtime secara otomatis mencari xib dan memuatnya. Anda dapat menggunakan trik ini untuk segala jenis tampilan kontainer, atau tampilan konten.
sumber
Saya berpikir
alternative
untuk menggunakanXIB views
untuk digunakanView Controller
dalam storyboard terpisah .Kemudian di storyboard utama di tempat tampilan kustom digunakan
container view
denganEmbed Segue
dan harus controller tampilan kustomStoryboardReference
ini yang tampilan harus ditempatkan di dalam tampilan lain di storyboard utama.Kemudian kita dapat mengatur delegasi dan komunikasi antara ViewController embed ini dan pengendali tampilan utama melalui persiapan untuk segue . Pendekatan ini berbeda daripada menampilkan UIView, tetapi jauh lebih sederhana dan lebih efisien (dari perspektif pemrograman) dapat digunakan untuk mencapai tujuan yang sama, yaitu memiliki tampilan kustom yang dapat digunakan kembali yang terlihat di storyboard utama
Keuntungan tambahan adalah Anda dapat menerapkan logika Anda di kelas CustomViewController dan mengatur semua delegasi dan melihat persiapan tanpa membuat kelas pengontrol yang terpisah (sulit ditemukan di proyek), dan tanpa menempatkan kode boilerplate di UIViewController utama menggunakan Komponen. Saya pikir ini bagus untuk komponen bekas yang dapat digunakan kembali. Komponen Music Player (seperti widget) yang dapat disematkan dalam tampilan lain.
sumber
Meskipun jawaban paling populer bekerja dengan baik, secara konsep mereka salah. Mereka semua digunakan
File's owner
sebagai koneksi antara outlet kelas dan komponen UI.File's owner
seharusnya digunakan hanya untuk objek tingkat atas bukanUIView
s. Lihat dokumen pengembang Apple . Memiliki UIView sebagaiFile's owner
mengarah pada konsekuensi yang tidak diinginkan ini.contentView
tempat yang seharusnya Anda gunakanself
. Ini tidak hanya jelek, tetapi juga salah secara struktural karena tampilan perantara membuat struktur data tidak menyampaikan struktur UI-nya. Ini seperti kebalikan dari UI deklaratif.Ada cara yang elegan untuk melakukannya tanpa menggunakan
File's owner
. Silakan periksa posting blog ini . Ini menjelaskan cara melakukannya dengan cara yang benar.sumber
Inilah jawaban yang Anda inginkan selama ini. Anda bisa saja membuat
CustomView
kelas Anda , memiliki instance master dalam xib dengan semua subview dan outlet. Kemudian Anda bisa menerapkan kelas itu ke instance apa pun di storyboard Anda atau xib lainnya.Tidak perlu mengutak-atik Pemilik File, atau menghubungkan outlet ke proxy atau memodifikasi xib dengan cara yang aneh, atau menambahkan contoh tampilan kustom Anda sebagai subview dari itu sendiri.
Lakukan saja ini:
UIView
keNibView
(atau dariUITableViewCell
keNibTableViewCell
)Itu dia!
Ia bahkan bekerja dengan IBDesignable untuk merujuk tampilan kustom Anda (termasuk subview dari xib) pada waktu desain di storyboard.
Anda dapat membaca lebih lanjut tentang ini di sini: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155
Dan Anda bisa mendapatkan kerangka BFWControls open source di sini: https://github.com/BareFeetWare/BFWControls
Dan inilah ekstrak sederhana dari
NibReplaceable
kode yang mendorongnya, jika Anda penasaran: https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4Tom 👣
sumber
Solusi ini dapat digunakan bahkan jika kelas Anda tidak memiliki nama yang sama dengan XIB. Misalnya, jika Anda memiliki pengontrol kelas tampilan dasar controllerA yang memiliki XIB nama controllerA.xib dan Anda subclass ini dengan controllerB dan ingin membuat turunan controllerB di storyboard, maka Anda dapat:
*
sumber
Solusi untuk Objective-C sesuai dengan langkah-langkah yang dijelaskan dalam respons Ben Patch .
Gunakan ekstensi untuk UIView:
Buat file
MyView.h
,MyView.m
danMyView.xib
.Pertama, siapkan tanggapan Anda
MyView.xib
seperti yang dikatakan Ben Patch, jadi atur kelasMyView
untuk pemilik File alih-alih tampilan utama di dalam XIB ini.MyView.h
:MyView.m
:Dan nanti buat saja tampilan Anda secara terprogram:
Peringatan! Pratinjau tampilan ini tidak akan ditampilkan di Storyboard jika Anda menggunakan Ekstensi WatchKit karena bug ini di Xcode> = 9.2: https://forums.developer.apple.com/thread/95616
sumber