鉴于一个观点,我怎么得到它的viewController?
我有一个指向UIView
的指针。 我如何访问它的UIViewController
? [self superview]
是另一个UIView
,但不是UIViewController
,对不对?
是的, superview
观点是包含你的观点的观点。 你的视图不应该知道它的视图控制器到底是什么,因为这会打破MVC的原则。
另一方面,控制器知道它负责哪个视图( self.view = myView
),通常这个视图委托处理控制器的方法/事件。
通常,而不是一个指向视图的指针,你应该有一个指向你的控制器的指针,而这个指针又可以执行一些控制逻辑,或者传递一些控制逻辑。
从nextResponder
的UIResponder
文档:
UIResponder类不会自动存储或设置下一个响应者,而是默认返回nil。 子类必须重写此方法来设置下一个响应者。 UIView通过返回pipe理它的UIViewController对象(如果有的话)或者它的超级视图(如果没有)来实现这个方法 。 UIViewController通过返回其视图的超级视图来实现该方法; UIWindow返回应用程序对象,UIApplication返回nil。
所以,如果你recursion视图的nextResponder
直到它是UIViewController
types,那么你有任何视图的父视图控制器。
请注意,它仍然可能没有父视图控制器。 但只有当视图不是viewController的视图的视图层次的一部分。
斯威夫特3延伸:
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扩展:
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类别:
@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
这个macros观避免了类别污染:
#define UIViewParentController(__view) ({ \ UIResponder *__responder = __view; \ while ([__responder isKindOfClass:[UIView class]]) \ __responder = [__responder nextResponder]; \ (UIViewController *)__responder; \ })
仅出于debugging目的,您可以调用_viewDelegate
上的_viewDelegate
来获取其视图控制器。 这是私人的API,所以对于App Store来说不是安全的,但对于debugging它是有用的。
其他有用的方法:
-
_viewControllerForAncestor
– 获取pipe理_viewControllerForAncestor
链中的视图的第一个控制器。 (谢谢n00neimp0rtant) -
_rootAncestorViewController
– 获取当前在窗口中设置视图层级的祖先控制器。
为了得到具有UIView的UIViewController的引用,你可以扩展UIResponder(这是UIView和UIViewController的超类),它允许通过响应者链上传UIViewController(否则返回nil)。
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中的快速和通用的方法:
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) }
如果您对代码不熟悉,并且想要查找给定视图的ViewController,那么您可以尝试:
- 在debugging中运行应用程序
- 导航到屏幕
- 开始查看检查器
- 抓住你想find的视图(或更好地看待子视图)
- 从右侧窗格中获取地址(例如0x7fe523bd3000)
- 在debugging控制台中开始编写命令:
po(UIView *)0x7fe523bd3000 po [(UIView *)0x7fe523bd3000 nextResponder] po [[(UIView *)0x7fe523bd3000 nextResponder] nextResponder] po [[[(UIView *)0x7fe523bd3000 nextResponder] nextResponder] nextResponder] ...
在大多数情况下,你会得到UIView,但不时会有基于UIViewController的类。
我认为你可以传播水龙头视图控制器,让它来处理它。 这是比较可以接受的方法。 至于从视图访问视图控制器,你应该保持对视图控制器的引用,因为没有别的办法。 看到这个线程,它可能有所帮助: 从视图访问视图控制器
Swift 3.0的更多types安全代码
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 } }
唉,这是不可能的,除非你inheritance视图,并提供一个实例属性或类似的存储视图控制器引用一旦视图添加到场景中…
在大多数情况下 – 解决这篇文章的原始问题是非常容易的,因为大多数视图控制器是程序员的知名实体,负责将任何子视图添加到ViewController的视图;-)这就是为什么我猜测苹果从来不打扰添加该属性。
有点晚,但是这里有一个扩展,使你能够find任何types的响应者,包括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 } }
如果设置断点,则可以将其粘贴到debugging器中以打印视图层次结构:
po [[UIWindow keyWindow] recursiveDescription]
你应该能够find你的视图的父母在那个混乱的地方:)