Katakanlah saya memiliki storyboard yang berisi UINavigationController
sebagai pengontrol tampilan awal. Pengontrol tampilan akarnya adalah subkelas UITableViewController
, yaitu BasicViewController
. Itu memiliki IBAction
yang terhubung ke tombol navigasi kanan dari bilah navigasi
Dari sana saya ingin menggunakan storyboard sebagai template untuk tampilan lain tanpa harus membuat storyboard tambahan. Katakanlah tampilan ini akan memiliki antarmuka yang persis sama tetapi dengan pengontrol tampilan root dari kelas SpecificViewController1
dan SpecificViewController2
yang merupakan subkelas dari BasicViewController
.
Kedua pengontrol tampilan tersebut akan memiliki fungsionalitas dan antarmuka yang sama kecuali untuk IBAction
metode.
Ini akan menjadi seperti berikut:
@interface BasicViewController : UITableViewController
@interface SpecificViewController1 : BasicViewController
@interface SpecificViewController2 : BasicViewController
Bisakah saya melakukan sesuatu seperti itu?
Bisakah saya membuat instance storyboard BasicViewController
tetapi memiliki pengontrol tampilan root ke subkelas SpecificViewController1
dan SpecificViewController2
?
Terima kasih.
Jawaban:
pertanyaan bagus - tapi sayangnya hanya jawaban yang timpang. Saya tidak percaya bahwa saat ini mungkin untuk melakukan apa yang Anda usulkan karena tidak ada penginisialisasi di UIStoryboard yang memungkinkan penggantian pengontrol tampilan yang terkait dengan storyboard seperti yang ditentukan dalam detail objek di storyboard saat inisialisasi. Pada inisialisasi semua elemen UI di stoaryboard ditautkan ke propertinya di pengontrol tampilan.
Ini akan secara default diinisialisasi dengan pengontrol tampilan yang ditentukan dalam definisi storyboard.
Jika Anda mencoba untuk mendapatkan kembali elemen UI yang Anda buat di storyboard, mereka masih harus ditautkan atau dikaitkan ke properti yang pernah digunakan pengontrol tampilan agar dapat "memberi tahu" pengontrol tampilan tentang peristiwa.
Ini bukan masalah besar menyalin tata letak storyboard terutama jika Anda hanya memerlukan desain yang serupa untuk 3 tampilan, namun jika Anda melakukannya, Anda harus memastikan bahwa semua asosiasi sebelumnya dihapus, atau akan mengalami crash saat mencoba untuk berkomunikasi dengan pengontrol tampilan sebelumnya. Anda akan dapat mengenalinya sebagai pesan kesalahan KVO di keluaran log.
Beberapa pendekatan yang dapat Anda lakukan:
simpan elemen UI dalam UIView - dalam file xib dan buat instance dari kelas dasar Anda dan tambahkan sebagai sub tampilan di tampilan utama, biasanya self.view. Kemudian Anda cukup menggunakan tata letak storyboard dengan pengontrol tampilan yang pada dasarnya kosong memegang tempatnya di storyboard tetapi dengan sub kelas pengontrol tampilan yang ditetapkan untuk mereka. Karena mereka akan mewarisi dari pangkalan, mereka akan mendapatkan pandangan itu.
buat tata letak dalam kode dan instal dari pengontrol tampilan dasar Anda. Jelas pendekatan ini mengalahkan tujuan penggunaan storyboard, tetapi mungkin cara yang tepat untuk kasus Anda. Jika Anda memiliki bagian lain dari aplikasi yang akan mendapat manfaat dari pendekatan papan cerita, tidak apa-apa untuk menyimpang di sana-sini jika perlu. Dalam kasus ini, seperti di atas, Anda hanya akan menggunakan pengontrol tampilan bank dengan subkelas Anda ditetapkan dan membiarkan pengontrol tampilan dasar menginstal UI.
Akan lebih baik jika Apple menemukan cara untuk melakukan apa yang Anda usulkan, tetapi masalah memiliki elemen grafis yang telah ditautkan sebelumnya dengan subkelas pengontrol masih akan menjadi masalah.
Selamat tahun baru!! dengan baik
sumber
Kode baris yang kita cari adalah:
Di Storyboard -> tambahkan UIViewController berikan nama kelas ParentVC.
sumber
class func instantiate() -> SubClass { let instance = (UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("SuperClass") as? SuperClass)! object_setClass(instance, SubClass.self) return (instance as? SubClass)! }
self
tidak berubah. Jadi pemeriksaan objek (misalnya membaca _ivar / nilai properti) setelahnyaobject_setClass
dapat menyebabkan crash.Seperti yang dinyatakan oleh jawaban yang diterima, sepertinya tidak mungkin dilakukan dengan papan cerita.
Solusi saya adalah menggunakan Nib - sama seperti dev yang menggunakannya sebelum storyboard. Jika Anda ingin memiliki pengontrol tampilan yang dapat digunakan kembali, subclassable (atau bahkan tampilan), rekomendasi saya adalah menggunakan Nibs.
Ketika Anda menghubungkan semua outlet Anda ke "Pemilik File" di
MyViewController.xib
Anda TIDAK menentukan kelas Nib apa yang harus dimuat, Anda hanya menentukan pasangan kunci-nilai: " tampilan ini harus terhubung ke nama variabel instance ini ." Saat memanggil[SubclassMyViewController alloc] initWithNibName:
proses inisialisasi menentukan pengontrol tampilan apa yang akan digunakan untuk " mengontrol " tampilan yang Anda buat di ujung pena.sumber
Storyboard dapat membuat instance subclass berbeda dari pengontrol tampilan kustom, meskipun ini melibatkan teknik yang sedikit tidak ortodoks: mengganti
alloc
metode untuk pengontrol tampilan. Saat pengontrol tampilan kustom dibuat, metode alokasi yang diganti sebenarnya mengembalikan hasil dari berjalanalloc
pada subkelas.Saya harus mengawali jawaban dengan syarat bahwa, meskipun saya telah mengujinya dalam berbagai skenario dan tidak menerima kesalahan, saya tidak dapat memastikan bahwa itu akan mengatasi pengaturan yang lebih kompleks (tetapi saya tidak melihat alasan mengapa tidak berfungsi) . Juga, saya belum mengirimkan aplikasi apa pun menggunakan metode ini, jadi ada kemungkinan luar bahwa itu mungkin ditolak oleh proses peninjauan Apple (meski sekali lagi saya tidak melihat alasan mengapa harus).
Untuk tujuan demonstrasi, saya memiliki subkelas yang
UIViewController
disebutTestViewController
, yang memiliki IBOutlet UILabel, dan IBAction. Di storyboard saya, saya telah menambahkan pengontrol tampilan dan mengubah kelasnya menjadiTestViewController
, dan menghubungkan IBOutlet ke UILabel dan IBAction ke UIButton. Saya menyajikan TestViewController melalui segue modal yang dipicu oleh UIButton pada viewController sebelumnya.Untuk mengontrol kelas mana yang dipakai, saya telah menambahkan variabel statis dan metode kelas terkait jadi dapatkan / setel subkelas yang akan digunakan (saya kira seseorang dapat mengadopsi cara lain untuk menentukan subkelas mana yang akan dipakai):
TestViewController.m:
Untuk pengujian saya, saya memiliki dua subclass dari
TestViewController
:RedTestViewController
danGreenTestViewController
. Masing-masing subclass memiliki properti tambahan dan masing-masing menggantiviewDidLoad
untuk mengubah warna latar belakang tampilan dan memperbarui teks UILabel IBOutlet:RedTestViewController.m:
GreenTestViewController.m:
Pada beberapa kesempatan saya mungkin ingin membuat contoh
TestViewController
sendiri, pada kesempatan lainRedTestViewController
atauGreenTestViewController
. Di pengontrol tampilan sebelumnya, saya melakukan ini secara acak sebagai berikut:Perhatikan bahwa
setClassForStoryBoard
metode ini memeriksa untuk memastikan bahwa nama kelas yang diminta memang merupakan subkelas TestViewController, untuk menghindari campur-aduk. Referensi di atasBlueTestViewController
untuk menguji fungsionalitas ini.sumber
coba ini, setelah instantiateViewControllerWithIdentifier.
Suka :
sumber
EXC_BAD_ACCESS
, jadi tidak merekomendasikan ini.init
juga tidak akan dipanggil. Batasan seperti itu membuat semua pendekatan tidak dapat digunakan.Mendasarkan terutama pada jawaban nickgzzjr dan Jiří Zahálka ditambah komentar di bawah yang kedua dari CocoaBob, saya telah menyiapkan metode umum singkat yang melakukan persis apa yang dibutuhkan OP. Anda hanya perlu memeriksa nama storyboard dan ID storyboard View Controllers
Opsional ditambahkan untuk menghindari pembongkaran paksa (peringatan swiftlint), tetapi metode mengembalikan objek yang benar.
sumber
Meskipun ini bukan subkelas, Anda dapat:
Berikut adalah contoh dari tutorial Bloc yang saya tulis,
ViewController
dengan subclassingWhiskeyViewController
:Ini memungkinkan Anda membuat subkelas dari subkelas pengontrol tampilan di storyboard. Anda kemudian dapat menggunakan
instantiateViewControllerWithIdentifier:
untuk membuat subclass tertentu.Pendekatan ini agak tidak fleksibel: modifikasi selanjutnya dalam storyboard ke pengontrol kelas dasar tidak menyebar ke subkelas. Jika Anda memiliki banyak subclass, Anda mungkin lebih baik menggunakan salah satu solusi lain, tetapi ini akan dilakukan dalam keadaan darurat.
sumber
initWithCoder:
, tidak memiliki hubungan yang diwariskan. Jenis hubungan ini tidak didukung oleh file storyboard.Metode Objc_setclass tidak membuat turunan dari childvc. Tapi saat muncul dari childvc, deinit dari childvc sedang dipanggil. Karena tidak ada memori yang dialokasikan secara terpisah untuk childvc, aplikasi macet. Pengontrol dasar memiliki sebuah instance, sedangkan anak vc tidak memiliki.
sumber
Jika Anda tidak terlalu bergantung pada storyboard, Anda dapat membuat file .xib terpisah untuk pengontrol.
Atur Pemilik File dan outlet yang sesuai ke
MainViewController
dan timpainit(nibName:bundle:)
di VC Utama sehingga anak-anaknya dapat mengakses Nib dan outletnya yang sama.Kode Anda akan terlihat seperti ini:
Dan Child VC Anda akan dapat menggunakan kembali ujung pena orang tuanya:
sumber
Mengambil jawaban dari sana-sini, saya menemukan solusi yang rapi ini.
Buat pengontrol tampilan orang tua dengan fungsi ini.
Hal ini memungkinkan compiler untuk memastikan bahwa pengontrol tampilan anak mewarisi dari pengontrol tampilan induk.
Kemudian kapan pun Anda ingin beralih ke pengontrol ini menggunakan sub kelas, Anda dapat melakukan:
Bagian kerennya adalah Anda bisa menambahkan referensi storyboard ke dirinya sendiri, lalu terus memanggil pengontrol tampilan anak "berikutnya".
sumber
Mungkin cara paling fleksibel adalah menggunakan tampilan yang dapat digunakan kembali.
(Buat Tampilan dalam file XIB terpisah atau
Container view
dan tambahkan ke setiap adegan pengontrol tampilan subkelas di storyboard)sumber
Ada solusi sehari-hari yang sederhana dan jelas.
Cukup letakkan storyboard / pengontrol yang ada di dalam storyobard / pengontrol baru. IE sebagai tampilan kontainer.
Ini adalah konsep yang sama persis dengan "subclassing", untuk, pengontrol tampilan.
Semuanya bekerja persis seperti di subclass.
Sama seperti Anda biasanya menempatkan subview tampilan di dalam tampilan lain , biasanya Anda biasanya menempatkan pengontrol tampilan di dalam pengontrol tampilan lain .
Bagaimana lagi Anda bisa melakukannya?
Ini adalah bagian dasar dari iOS, sesederhana konsep "subview".
Semudah ini ...
Anda sekarang jelas harus
list
melakukan apa pun yang Anda inginkandll.
Tampilan penampung "seperti" membuat subkelas dengan cara yang sama seperti "sub-tampilan" adalah "seperti" subkelas.
Tentu saja jelas, Anda tidak bisa "menyubkass sebuah layout" - apa artinya itu?
("Subclassing" berhubungan dengan perangkat lunak OO dan tidak memiliki koneksi ke "tata letak".)
Tentunya saat Anda ingin menggunakan kembali tampilan, Anda cukup men-subviewnya di dalam tampilan lain.
Saat Anda ingin menggunakan kembali tata letak pengontrol, Anda hanya perlu menampilkannya di dalam pengontrol lain.
Ini seperti mekanisme paling dasar dari iOS !!
Catatan - selama bertahun-tahun sekarang sangatlah mudah untuk secara dinamis memuat pengontrol tampilan lain sebagai tampilan penampung. Dijelaskan di bagian terakhir: https://stackoverflow.com/a/23403979/294884
Catatan - "_sb" hanyalah makro yang kami gunakan untuk menyimpan pengetikan,
sumber
Terima kasih atas jawaban inspiratif @ Jiří Zahálka, saya menjawab solusi saya 4 tahun yang lalu di sini , tetapi @Sayka menyarankan saya untuk mempostingnya sebagai jawaban, jadi ini dia.
Dalam proyek saya, biasanya, jika saya menggunakan Storyboard untuk subkelas UIViewController, saya selalu menyiapkan metode statis yang disebut
instantiate()
dalam subkelas itu, untuk membuat instance dari Storyboard dengan mudah. Jadi untuk menjawab pertanyaan OP, jika kita ingin berbagi Storyboard yang sama untuk subclass yang berbeda, kita bisa langsungsetClass()
ke contoh itu sebelum mengembalikannya.sumber
Komentar Cocoabob dari jawaban Jiří Zahálka membantu saya mendapatkan solusi ini dan berhasil dengan baik.
sumber