İçin bir işaretçim var UIView
. Nasıl erişebilirim UIViewController
? [self superview]
başka UIView
, ama değil UIViewController
, değil mi?
İçin bir işaretçim var UIView
. Nasıl erişebilirim UIViewController
? [self superview]
başka UIView
, ama değil UIViewController
, değil mi?
Yanıtlar:
Evet, superview
görünümünüzü içeren görünümdür. Görüşünüz tam olarak hangisinin görünüm denetleyicisi olduğunu bilmemelidir, çünkü bu MVC ilkelerini ihlal edecektir.
Öte yandan, denetleyici hangi görünümden sorumlu olduğunu ( self.view = myView
) bilir ve genellikle bu görünüm denetleyiciye yönelik yöntemleri / olayları temsil eder.
Genellikle, görünümünüze bir işaretçi yerine, denetleyicinize bir işaretçi olmalıdır, bu da kontrol eden bir mantık yürütebilir veya görünümüne bir şey iletebilir.
İçin UIResponder
belgelerden nextResponder
:
UIResponder sınıfı, bir sonraki yanıtlayıcıyı otomatik olarak kaydetmez veya ayarlamaz, bunun yerine varsayılan olarak sıfır döndürür. Bir sonraki yanıtlayıcıyı ayarlamak için alt sınıfların bu yöntemi geçersiz kılması gerekir. UIView bu yöntemi, onu yöneten UIViewController nesnesini (varsa) veya denetimini (yoksa) döndürerek uygular ; UIViewController yöntemi bakış açısının denetimine döndürerek uygular; UIWindow uygulama nesnesini ve UIApplication nil değerini döndürür.
Bu nedenle, bir görünümün nextResponder
türüne ulaşıncaya kadar UIViewController
okuyorsanız, herhangi bir görünümün üst görünümü var.
Yine de üst görünüm denetleyicisine sahip olmayabileceğini unutmayın . Ancak yalnızca görünüm bir viewController görünümünün görünüm hiyerarşisinin bir parçası değilse.
Swift 3 ve Swift 4.1 uzantısı:
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
}
}
Swift 2 uzantısı:
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
}
}
Objective-C kategorisi:
@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
Bu makro kategori kirliliğini önler:
#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})
UIResponder
;) ' nin bir uzantısı yapmalısınız . Çok düzenleme sonrası.
Bir satırda @andrey yanıtı ( Swift 4.1'de test edilmiştir ):
extension UIResponder {
public var parentViewController: UIViewController? {
return next as? UIViewController ?? next?.parentViewController
}
}
kullanımı:
let vc: UIViewController = view.parentViewController
parentViewController
public
uzantısı ile aynı dosya ise tanımlanamaz UIView
onu ayarlayabilirsiniz fileprivate
, derler ama çalışmıyor! 😐
Yalnızca hata ayıklama amacıyla _viewDelegate
, görünüm denetleyicilerini almak için görünümleri arayabilirsiniz . Bu özel API'dir, bu nedenle App Store için güvenli değildir, ancak hata ayıklama için kullanışlıdır.
Diğer faydalı yöntemler:
_viewControllerForAncestor
- denetim zincirindeki bir görünümü yöneten ilk denetleyiciyi edinin. (teşekkürler n00neimp0rtant)_rootAncestorViewController
- görünüm hiyerarşisi şu anda pencerede ayarlanmış olan ata kontrolörünü alın._viewControllerForAncestor
bir görünüm denetleyicisine ait olan ilk tanesini bulana kadar denetimleri geçecektir.
UIView'e sahip UIViewController'a başvurmak için, yanıtlayıcı zincirinden yukarı çıkıp UIViewController'a (aksi halde sıfır) ulaşan UIResponder (UIView ve UIViewController için süper sınıftır) uzantısını yapabilirsiniz.
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
Swift 3'te hızlı ve genel yol:
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)
}
Kodu bilmiyorsanız ve verilen görünüme karşılık gelen ViewController'ı bulmak istiyorsanız, deneyebilirsiniz:
po (UIView *) 0x7fe523bd3000 po [(UIView *) 0x7fe523bd3000 nextResponder] po [[(UIView *) 0x7fe523bd3000 nextResponder] nextResponder] po [[[(UIView *) 0x7fe523bd3000 nextResponder] nextResponder] nextResponder] ...
Çoğu durumda UIView alırsınız, ancak zaman zaman UIViewController tabanlı sınıf olur.
Musluğu görünüm denetleyicisine yayabilir ve işlemesine izin verebilirsiniz. Bu daha kabul edilebilir bir yaklaşımdır. Bir görünüm denetleyicisine kendi görünümünden erişmeye gelince, başka bir yol olmadığından bir görünüm denetleyicisine başvurmanız gerekir. Bu konuya bakın, yardımcı olabilir: Görünüm denetleyicisine bir görünümden erişme
Swift 3.0 için daha fazla tür güvenlik kodu
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
}
}
Ne yazık ki, görünümü alt sınıfa ayırmadıkça ve görünüm sahneye eklendikten sonra görünüm denetleyicisi başvurusunu depolayan bir örnek özelliği veya benzerleri sağlamadığınız sürece bu mümkün değildir ...
Çoğu durumda - çoğu görünüm denetleyicisi, ViewController'ın Görünümüne herhangi bir alt görünüm eklemekten sorumlu olan programcıya iyi bilinen varlıklar olduğundan, bu yazının orijinal sorunu etrafında çalışmak çok kolaydır.Bu yüzden Apple'ın bu özelliği eklemek için hiç uğraşmadım.
Biraz geç, ancak işte ViewController dahil her türden bir yanıtlayıcı bulmanızı sağlayan bir uzantı.
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
}
}
Bir kesme noktası ayarlarsanız, görünüm hiyerarşisini yazdırmak için bunu hata ayıklayıcıya yapıştırabilirsiniz:
po [[UIWindow keyWindow] recursiveDescription]
Bu karmaşa içinde görünümünüzün ebeveynini bir yerde bulabilmeniz gerekir :)
recursiveDescription
görünüm denetleyicilerini değil, yalnızca görünüm hiyerarşisini yazdırır .