我有一个指向UIView
的指针。如何访问它的UIViewController
?[self superview]
是另一个UIView
,但不是UIViewController
,对吗?
我有一个指向UIView
的指针。如何访问它的UIViewController
?[self superview]
是另一个UIView
,但不是UIViewController
,对吗?
根据 UIResponder
的文档,nextResponder
方法不会自动存储或设置下一个响应者,而是默认返回 nil。子类必须重写此方法来设置下一个响应者。 UIView 实现此方法,通过返回管理它的 UIViewController 对象(如果有)或其父视图(如果没有)来设置下一个响应者;UIViewController 通过返回其视图的父视图来实现该方法;UIWindow 返回应用程序对象,UIApplication 返回 nil。
因此,如果您递归查找视图的 nextResponder
直到它是 UIViewController
类型,则可以获得任何视图的父视图控制器。
请注意,它仍然可能 没有 父视图控制器。但这只有在视图不属于视图控制器的视图层次结构时才会发生。
Swift 3 和 Swift 4.1 扩展:
extension UIView {
var parentViewController: UIViewController? {
// Starts from next (As we know self is not a UIViewController).
var parentResponder: UIResponder? = self.next
while parentResponder != nil {
if let viewController = parentResponder as? UIViewController {
return viewController
}
parentResponder = parentResponder?.next
}
return nil
}
}
Swift 2扩展:
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self.nextResponder()
while parentResponder != nil {
if let viewController = parentResponder as? UIViewController {
return viewController
}
parentResponder = parentResponder!.nextResponder()
}
return nil
}
}
Objective-C 类别(category):
@interface UIView (mxcl)
- (UIViewController *)parentViewController;
@end
@implementation UIView (mxcl)
- (UIViewController *)parentViewController {
UIResponder *responder = [self nextResponder];
while (responder != nil) {
if ([responder isKindOfClass:[UIViewController class]]) {
return (UIViewController *)responder;
}
responder = [responder nextResponder];
}
return nil;
}
@end
这个宏避免了类别污染:
#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})
UIResponder
的扩展 ;)。非常有启发性的文章。 - ScottyBlades@andrey 的一行回答(在Swift 4.1中测试):
extension UIResponder {
public var parentViewController: UIViewController? {
return next as? UIViewController ?? next?.parentViewController
}
}
用法:
let vc: UIViewController = view.parentViewController
parentViewController
不能被定义为public
,您可以将其设置为fileprivate
,虽然它可以编译但是无法正常工作! - user5281587是的,superview
是包含你的视图的视图。你的视图不应该知道它的视图控制器是哪一个,因为那会违反 MVC 原则。
另一方面,控制器知道它所负责的视图 (self.view = myView
),通常这个视图会委托方法 / 事件处理给控制器。
通常情况下,你应该有一个指向你的控制器的指针,控制器再执行一些控制逻辑或者将某些东西传递给它的视图。
_viewDelegate
来获取它们的视图控制器。这是私有API,因此不适合App Store,但对于调试非常有用。_viewControllerForAncestor
- 获取在父视图链中管理视图的第一个控制器。(感谢n00neimp0rtant)_rootAncestorViewController
- 获取祖先控制器,其视图层次结构设置在当前窗口中。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)
}
po (UIView *)0x7fe523bd3000 po [(UIView *)0x7fe523bd3000 nextResponder] po [[(UIView *)0x7fe523bd3000 nextResponder] nextResponder] po [[[(UIView *)0x7fe523bd3000 nextResponder] nextResponder] nextResponder] ...通常情况下,您将获得UIView,但有时会出现基于UIViewController的类。
我认为你可以将tap事件传递给视图控制器,并让它处理。这是更可接受的方法。至于从视图中访问视图控制器,你应该保持对视图控制器的引用,因为没有其他方式。参见此线程,它可能有所帮助:从视图访问视图控制器
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
}
}