Diberikan tampilan, bagaimana cara mendapatkan viewController?
141
Saya memiliki pointer ke a UIView. Bagaimana cara saya mengaksesnya UIViewController? [self superview]ada yang lain UIView, tapi bukan UIViewController, kan?
Pada dasarnya, saya mencoba untuk memanggil viewController's viewWillAppear karena pandangan saya ditolak. Tampilan ditolak oleh tampilan itu sendiri yang mendeteksi keran dan memanggil [self removeFromSuperview]; ViewController tidak memanggil viewWillAppear / WillDisappear / DidAppear / DidDisappear itu sendiri.
mahboudz
Maksud saya, saya mencoba memanggil viewWillDisappear karena pandangan saya ditolak.
Ya, superviewini adalah tampilan yang berisi tampilan Anda. Pandangan Anda seharusnya tidak tahu yang mana persisnya pengontrol tampilan, karena itu akan melanggar prinsip MVC.
Kontroler, di sisi lain, tahu tampilan mana yang bertanggung jawab untuk ( self.view = myView), dan biasanya, tampilan ini mendelegasikan metode / kejadian untuk penanganan ke controller.
Biasanya, alih-alih sebuah penunjuk ke tampilan Anda, Anda harus memiliki penunjuk ke pengontrol Anda, yang pada gilirannya dapat menjalankan beberapa logika kontrol, atau meneruskan sesuatu ke pandangannya.
Saya tidak yakin apakah itu akan melanggar prinsip-prinsip MVC. Pada satu titik, tampilan hanya memiliki satu pengontrol tampilan. Mampu sampai ke sana untuk meneruskan pesan kembali ke itu, harus menjadi fitur otomatis, bukan di mana Anda harus bekerja untuk mencapainya (dengan menambahkan properti untuk melacak). Orang dapat mengatakan hal yang sama tentang pandangan: mengapa Anda perlu tahu siapa anak Anda? Atau apakah ada pandangan saudara lain. Namun ada cara untuk mendapatkan benda-benda itu.
mahboudz
Anda agak benar tentang tampilan, mengetahui tentang induknya, itu bukan keputusan desain super-jelas, tetapi sudah ditetapkan untuk melakukan beberapa tindakan, menggunakan langsung variabel anggota superview (periksa tipe induk, hapus dari induk, dll). Setelah bekerja dengan PureMVC baru-baru ini, saya menjadi sedikit lebih rewel tentang abstraksi desain :) Saya akan membuat paralel antara kelas UIView dan UIViewController iPhone dan kelas Tampilan dan Mediator PureMVC - sebagian besar waktu, kelas tampilan tidak perlu tahu tentang MVC handler / interface-nya (UIViewController / Mediator).
Dimitar Dimitrov
9
Kata kunci: "kebanyakan".
Glenn Maynard
278
Dari UIResponderdokumentasi untuk nextResponder:
Kelas UIResponder tidak menyimpan atau mengatur responden berikutnya secara otomatis, sebaliknya mengembalikan nol secara default. Subclass harus mengganti metode ini untuk mengatur responden berikutnya. UIView mengimplementasikan metode ini dengan mengembalikan objek UIViewController yang mengaturnya (jika ada) atau superview-nya (jika tidak) ; UIViewController mengimplementasikan metode dengan mengembalikan tampilan pandangannya; UIWindow mengembalikan objek aplikasi, dan aplikasi UIA mengembalikan nihil.
Jadi, jika Anda mengulang tampilan nextRespondersampai tipe UIViewController, maka Anda memiliki viewController induk view.
Perhatikan bahwa masih mungkin tidak memiliki pengontrol tampilan induk. Tetapi hanya jika tampilan bukan bagian dari hierarki tampilan viewController.
Hanya satu hal: jika Anda khawatir tentang polusi kategori, cukup definisikan sebagai fungsi statis daripada makro. Juga typecast brutal itu berbahaya, dan di atas semua itu makro mungkin tidak benar tetapi tidak yakin.
mojuba
Makro berfungsi, saya hanya menggunakan versi makro secara pribadi. Saya memulai banyak proyek dan memiliki header saya hanya mampir di mana-mana dengan sekelompok fungsi utilitas makro ini. Menghemat waktu saya. Jika Anda tidak suka makro, Anda bisa mengadaptasinya ke dalam suatu fungsi, tetapi fungsi statis sepertinya membosankan, karena Anda harus meletakkannya di setiap file yang ingin Anda gunakan. Sepertinya Anda ingin fungsi non-statis dideklarasikan di header dan didefinisikan dalam .m di suatu tempat?
mxcl
Senang menghindari beberapa delegasi atau pemberitahuan. Terima kasih!
Ferran Maylinch
Anda harus membuatnya menjadi ekstensi UIResponder;). Posting yang sangat meneguhkan.
ScottyBlades
32
@andrey menjawab dalam satu baris (diuji dalam Swift 4.1 ):
parentViewControllertidak dapat ditentukan publicjika ekstensi dalam file yang sama dengan Anda, UIViewAnda dapat mengaturnya fileprivate, itu mengkompilasi tetapi tidak bekerja! 😐
Ini bekerja dengan baik. Saya telah menggunakan ini untuk aksi tombol kembali di dalam file .xib header umum.
McDonal_11
23
Hanya untuk tujuan debug, Anda dapat memanggil _viewDelegatetampilan untuk mendapatkan pengontrol tampilan mereka. Ini adalah API pribadi, jadi tidak aman untuk App Store, tetapi untuk debugging itu berguna.
Metode berguna lainnya:
_viewControllerForAncestor- dapatkan pengontrol pertama yang mengelola tampilan dalam rantai superview. (terima kasih n00neimp0rtant)
_rootAncestorViewController - dapatkan pengendali leluhur yang hierarki pandangannya diatur di jendela saat ini.
Inilah mengapa saya sampai pada pertanyaan ini. Rupanya, 'nextResponder' melakukan hal yang sama, tetapi saya menghargai wawasan yang diberikan jawaban ini. Saya mengerti dan menyukai MVC, tetapi debugging adalah hewan yang berbeda!
mbm29414
4
Tampaknya ini hanya berfungsi pada tampilan utama pengontrol tampilan, bukan pada subview apa pun. _viewControllerForAncestorakan melintasi superview hingga menemukan yang pertama yang dimiliki pengontrol tampilan.
n00neimp0rtant
Terima kasih @ n00neimp0rtant! Saya membatalkan jawaban ini sehingga orang melihat komentar Anda.
eyuelt
Perbarui jawaban dengan lebih banyak metode.
Leo Natan
1
Ini bermanfaat saat melakukan debugging.
evanchin
10
Untuk mendapatkan referensi ke UIViewController yang memiliki UIView, Anda dapat membuat ekstensi UIResponder (yang merupakan kelas super untuk UIView dan UIViewController), yang memungkinkan untuk naik melalui rantai responden dan dengan demikian mencapai UIViewController (jika tidak mengembalikan nol).
Saya pikir Anda dapat menyebarkan keran ke controller tampilan dan membiarkannya menanganinya. Ini adalah pendekatan yang lebih dapat diterima. Adapun mengakses pengontrol tampilan dari pandangannya, Anda harus mempertahankan referensi ke pengontrol tampilan, karena tidak ada cara lain. Lihat utas ini, mungkin membantu:
Mengakses pengontrol tampilan dari tampilan
Jika Anda memiliki beberapa tampilan, dan satu menutup semua tampilan, dan Anda perlu memanggil viewWillDisappear, bukankah lebih mudah bagi pandangan itu untuk mendeteksi keran daripada menyerahkan keran ke pengontrol tampilan dan meminta pengontrol tampilan memeriksa dengan semua tampilan untuk melihat mana yang disadap?
Sayangnya, ini tidak mungkin kecuali Anda mensubclass view dan memberikannya dengan properti instance atau yang menyimpan referensi controller view di dalamnya begitu view ditambahkan ke scene ...
Dalam kebanyakan kasus - sangat mudah untuk mengatasi masalah asli posting ini karena sebagian besar pengontrol tampilan adalah entitas terkenal bagi programmer yang bertanggung jawab untuk menambahkan subview apa pun ke ViewController's View ;-) Itulah sebabnya saya menduga Apple tidak pernah repot untuk menambahkan properti itu.
Jawaban:
Ya,
superview
ini adalah tampilan yang berisi tampilan Anda. Pandangan Anda seharusnya tidak tahu yang mana persisnya pengontrol tampilan, karena itu akan melanggar prinsip MVC.Kontroler, di sisi lain, tahu tampilan mana yang bertanggung jawab untuk (
self.view = myView
), dan biasanya, tampilan ini mendelegasikan metode / kejadian untuk penanganan ke controller.Biasanya, alih-alih sebuah penunjuk ke tampilan Anda, Anda harus memiliki penunjuk ke pengontrol Anda, yang pada gilirannya dapat menjalankan beberapa logika kontrol, atau meneruskan sesuatu ke pandangannya.
sumber
Dari
UIResponder
dokumentasi untuknextResponder
:Jadi, jika Anda mengulang tampilan
nextResponder
sampai tipeUIViewController
, maka Anda memiliki viewController induk view.Perhatikan bahwa masih mungkin tidak memiliki pengontrol tampilan induk. Tetapi hanya jika tampilan bukan bagian dari hierarki tampilan viewController.
Ekstensi Swift 3 dan Swift 4.1 :
Ekstensi Swift 2:
Kategori Objective-C:
Makro ini menghindari polusi kategori:
sumber
UIResponder
;). Posting yang sangat meneguhkan.@andrey menjawab dalam satu baris (diuji dalam Swift 4.1 ):
pemakaian:
sumber
parentViewController
tidak dapat ditentukanpublic
jika ekstensi dalam file yang sama dengan Anda,UIView
Anda dapat mengaturnyafileprivate
, itu mengkompilasi tetapi tidak bekerja! 😐Hanya untuk tujuan debug, Anda dapat memanggil
_viewDelegate
tampilan untuk mendapatkan pengontrol tampilan mereka. Ini adalah API pribadi, jadi tidak aman untuk App Store, tetapi untuk debugging itu berguna.Metode berguna lainnya:
_viewControllerForAncestor
- dapatkan pengontrol pertama yang mengelola tampilan dalam rantai superview. (terima kasih n00neimp0rtant)_rootAncestorViewController
- dapatkan pengendali leluhur yang hierarki pandangannya diatur di jendela saat ini.sumber
_viewControllerForAncestor
akan melintasi superview hingga menemukan yang pertama yang dimiliki pengontrol tampilan.Untuk mendapatkan referensi ke UIViewController yang memiliki UIView, Anda dapat membuat ekstensi UIResponder (yang merupakan kelas super untuk UIView dan UIViewController), yang memungkinkan untuk naik melalui rantai responden dan dengan demikian mencapai UIViewController (jika tidak mengembalikan nol).
sumber
Cara cepat dan umum dalam Swift 3:
sumber
Jika Anda tidak terbiasa dengan kode dan Anda ingin menemukan ViewController sesuai dengan tampilan yang diberikan, maka Anda dapat mencoba:
Dalam kebanyakan kasus, Anda akan mendapatkan UIView, tetapi dari waktu ke waktu akan ada kelas berbasis UIViewController.
sumber
Saya pikir Anda dapat menyebarkan keran ke controller tampilan dan membiarkannya menanganinya. Ini adalah pendekatan yang lebih dapat diterima. Adapun mengakses pengontrol tampilan dari pandangannya, Anda harus mempertahankan referensi ke pengontrol tampilan, karena tidak ada cara lain. Lihat utas ini, mungkin membantu: Mengakses pengontrol tampilan dari tampilan
sumber
Jenis kode aman lainnya untuk Swift 3.0
sumber
Sayangnya, ini tidak mungkin kecuali Anda mensubclass view dan memberikannya dengan properti instance atau yang menyimpan referensi controller view di dalamnya begitu view ditambahkan ke scene ...
Dalam kebanyakan kasus - sangat mudah untuk mengatasi masalah asli posting ini karena sebagian besar pengontrol tampilan adalah entitas terkenal bagi programmer yang bertanggung jawab untuk menambahkan subview apa pun ke ViewController's View ;-) Itulah sebabnya saya menduga Apple tidak pernah repot untuk menambahkan properti itu.
sumber
Sedikit terlambat, tetapi ini adalah ekstensi yang memungkinkan Anda menemukan responden jenis apa pun, termasuk ViewController.
sumber
Jika Anda menetapkan breakpoint, Anda bisa menempelkan ini ke debugger untuk mencetak hierarki tampilan:
Anda harus dapat menemukan orang tua pandangan Anda di suatu tempat di kekacauan itu :)
sumber
recursiveDescription
hanya mencetak hierarki tampilan , bukan pengontrol tampilan.