鉴于一个观点,我如何获得它的viewController?

mah*_*udz 129 iphone debugging uiviewcontroller uiview ios

我有一个指针UIView.我如何访问它UIViewController[self superview]是另一个UIView,但不是UIViewController,对吧?

mxc*_*xcl 261

从以下UIResponder文档nextResponder:

UIResponder类不会自动存储或设置下一个响应程序,而是默认返回nil.子类必须覆盖此方法以设置下一个响应者.UIView通过返回管理它的UIViewController对象(如果有的话)或超级视图(如果没有)来实现此方法 ; UIViewController通过返回其视图的superview来实现该方法; UIWindow返回应用程序对象,UIApplication返回nil.

因此,如果你递归视图nextResponder直到它是类型UIViewController,那么你有任何视图的父viewController.

请注意,它仍然可能没有父视图控制器.但仅当视图不是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
    }
}
Run Code Online (Sandbox Code Playgroud)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

这个宏避免了类别污染:

#define UIViewParentController(__view) ({ \
    UIResponder *__responder = __view; \
    while ([__responder isKindOfClass:[UIView class]]) \
        __responder = [__responder nextResponder]; \
    (UIViewController *)__responder; \
})
Run Code Online (Sandbox Code Playgroud)


Dim*_*rov 40

是的,superview是包含您的视图的视图.您的视图不应该知道它的视图控制器究竟是哪个,因为这会破坏MVC原则.

另一方面,控制器知道它负责(self.view = myView)的视图,并且通常,该视图将用于处理的方法/事件委托给控制器.

通常,您应该有一个指向控制器的指针,而不是指向视图的指针,控制器可以执行某些控制逻辑,也可以将某些内容传递给其视图.

  • 我不确定它是否会违反MVC原则.在任何一点,视图只有一个视图控制器.能够到达它以便将消息传递给它,应该是一个自动功能,而不是你必须努力实现的功能(通过添加属性来跟踪).人们可以对观点说同样的话:为什么你需要知道你是谁?或者是否有其他兄弟视图.然而,有办法获得这些对象. (23认同)
  • 关键词:"最". (8认同)

Fed*_*llo 25

@andrey在一行中回答(在Swift 4.1中测试):

extension UIResponder {
    public var parentViewController: UIViewController? {
        return next as? UIViewController ?? next?.parentViewController
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

 let vc: UIViewController = view.parentViewController
Run Code Online (Sandbox Code Playgroud)


Leo*_*ica 22

仅出于调试目的,您可以调用_viewDelegate视图以获取其视图控制器.这是私有API,因此对App Store不安全,但对于调试它很有用.

其他有用的方法:

  • _viewControllerForAncestor - 获取管理superview链中视图的第一个控制器.(感谢n00neimp0rtant)
  • _rootAncestorViewController - 获取当前在窗口中设置视图层次结构的祖先控制器.

  • 它似乎只适用于视图控制器的主视图,而不适用于它的任何子视图.`_viewControllerForAncestor`将遍历超级视图,直到找到第一个属于视图控制器的超级视图. (4认同)

And*_*rey 9

要获得对具有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
Run Code Online (Sandbox Code Playgroud)


iTS*_*gar 7

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)
}
Run Code Online (Sandbox Code Playgroud)