我有一个指向的指针UIView
。如何访问它UIViewController
? [self superview]
是另一个UIView
,但不是UIViewController
,对吗?
我有一个指向的指针UIView
。如何访问它UIViewController
? [self superview]
是另一个UIView
,但不是UIViewController
,对吗?
Answers:
是的,superview
是包含您的视图的视图。您的视图不应确切知道其视图控制器是哪一个,因为这会破坏MVC原理。
另一方面,控制器知道它负责哪个视图(self.view = myView
),通常,该视图将用于处理的方法/事件委托给控制器。
通常,您应该有一个指向控制器的指针,而不是指向视图的指针,该控制器又可以执行一些控制逻辑,也可以将某些内容传递给它的视图。
从UIResponder
文档中nextResponder
:
UIResponder类不会自动存储或设置下一个响应者,而是默认返回nil。子类必须重写此方法才能设置下一个响应者。UIView通过返回管理它的UIViewController对象(如果有)或它的超级视图(如果没有)来实现此方法;UIViewController通过返回其视图的超级视图来实现该方法。UIWindow返回应用程序对象,而UIApplication返回nil。
因此,如果递归一个视图,nextResponder
直到它的类型为UIViewController
,则您将拥有任何视图的父视图控制器。
请注意,它可能仍然没有父视图控制器。但仅当视图不属于viewController的视图层次结构的一部分时。
Swift 3和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
}
}
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
此宏避免类别污染:
#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})
UIResponder
;)。非常具有启发性的帖子。
@andrey答案在一行中(在Swift 4.1中进行了测试):
extension UIResponder {
public var parentViewController: UIViewController? {
return next as? UIViewController ?? next?.parentViewController
}
}
用法:
let vc: UIViewController = view.parentViewController
仅出于调试目的,您可以调用_viewDelegate
视图以获取其视图控制器。这是私有API,因此对于App Store而言并不安全,但对于调试它很有用。
其他有用的方法:
_viewControllerForAncestor
-获得管理超级视图链中视图的第一个控制器。(感谢n00neimp0rtant)_rootAncestorViewController
-获取当前在窗口中设置其视图层次的祖先控制器。_viewControllerForAncestor
将遍历超级视图,直到找到属于视图控制器的第一个。
要获得具有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核心,则可以尝试:
po(UIView *)0x7fe523bd3000 po [(UIView *)0x7fe523bd3000 nextResponder] po [[(UIView *)0x7fe523bd3000 nextResponder] nextResponder] po [[[((UIView *)0x7fe523bd3000 nextResponder] nextResponder] nextResponder] ...
在大多数情况下,您将获得UIView,但有时会有基于UIViewController的类。
我认为您可以将水龙头传播到视图控制器并让其处理。这是更可接受的方法。至于从视图控制器访问视图控制器,您应该维护对视图控制器的引用,因为没有其他方法。请参阅此线程,它可能会有所帮助: 从视图访问视图控制器
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
}
}