Saya memiliki pointer ke a UIView
. Bagaimana cara saya mengaksesnya UIViewController
? [self superview]
ada yang lain UIView
, tapi bukan UIViewController
, kan?
Saya memiliki pointer ke a UIView
. Bagaimana cara saya mengaksesnya UIViewController
? [self superview]
ada yang lain UIView
, tapi bukan UIViewController
, kan?
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.
Dari UIResponder
dokumentasi 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 nextResponder
sampai 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.
Ekstensi Swift 3 dan Swift 4.1 :
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder?.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
Ekstensi Swift 2:
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.nextResponder()
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
Kategori Objective-C:
@interface UIView (mxcl)
- (UIViewController *)parentViewController;
@end
@implementation UIView (mxcl)
- (UIViewController *)parentViewController {
UIResponder *responder = self;
while ([responder isKindOfClass:[UIView class]])
responder = [responder nextResponder];
return (UIViewController *)responder;
}
@end
Makro ini menghindari polusi kategori:
#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})
UIResponder
;). Posting yang sangat meneguhkan.
@andrey menjawab dalam satu baris (diuji dalam Swift 4.1 ):
extension UIResponder {
public var parentViewController: UIViewController? {
return next as? UIViewController ?? next?.parentViewController
}
}
pemakaian:
let vc: UIViewController = view.parentViewController
parentViewController
tidak dapat ditentukan public
jika ekstensi dalam file yang sama dengan Anda, UIView
Anda dapat mengaturnya fileprivate
, 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._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).
extension UIResponder {
func getParentViewController() -> UIViewController? {
if self.nextResponder() is UIViewController {
return self.nextResponder() as? UIViewController
} else {
if self.nextResponder() != nil {
return (self.nextResponder()!).getParentViewController()
}
else {return nil}
}
}
}
//Swift 3
extension UIResponder {
func getParentViewController() -> UIViewController? {
if self.next is UIViewController {
return self.next as? UIViewController
} else {
if self.next != nil {
return (self.next!).getParentViewController()
}
else {return nil}
}
}
}
let vc = UIViewController()
let view = UIView()
vc.view.addSubview(view)
view.getParentViewController() //provide reference to vc
Cara cepat dan umum dalam Swift 3:
extension UIResponder {
func parentController<T: UIViewController>(of type: T.Type) -> T? {
guard let next = self.next else {
return nil
}
return (next as? T) ?? next.parentController(of: T.self)
}
}
//Use:
class MyView: UIView {
...
let parentController = self.parentController(of: MyViewController.self)
}
Jika Anda tidak terbiasa dengan kode dan Anda ingin menemukan ViewController sesuai dengan tampilan yang diberikan, maka Anda dapat mencoba:
po (UIView *) 0x7fe523bd3000 po [(UIView *) 0x7fe523bd3000 nextResponder] po [[((UIView *) 0x7fe523bd3000 nextResponder] nextResponder] po [[[(UIView *) 0x7fe523bd3000 nextResponder] nextResponder] nextResponder] ...
Dalam kebanyakan kasus, Anda akan mendapatkan UIView, tetapi dari waktu ke waktu akan ada kelas berbasis UIViewController.
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
Jenis kode aman lainnya untuk Swift 3.0
extension UIResponder {
func owningViewController() -> UIViewController? {
var nextResponser = self
while let next = nextResponser.next {
nextResponser = next
if let vc = nextResponser as? UIViewController {
return vc
}
}
return nil
}
}
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.
Sedikit terlambat, tetapi ini adalah ekstensi yang memungkinkan Anda menemukan responden jenis apa pun, termasuk ViewController.
extension NSObject{
func findNext(type: AnyClass) -> Any{
var resp = self as! UIResponder
while !resp.isKind(of: type.self) && resp.next != nil
{
resp = resp.next!
}
return resp
}
}
Jika Anda menetapkan breakpoint, Anda bisa menempelkan ini ke debugger untuk mencetak hierarki tampilan:
po [[UIWindow keyWindow] recursiveDescription]
Anda harus dapat menemukan orang tua pandangan Anda di suatu tempat di kekacauan itu :)
recursiveDescription
hanya mencetak hierarki tampilan , bukan pengontrol tampilan.