Given a view, how do I get its viewController? Given a view, how do I get its viewController? ios ios

Given a view, how do I get its viewController?


From the UIResponder documentation for nextResponder:

The UIResponder class does not store or set the next responder automatically, instead returning nil by default. Subclasses must override this method to set the next responder. UIView implements this method by returning the UIViewController object that manages it (if it has one) or its superview (if it doesn’t); UIViewController implements the method by returning its view’s superview; UIWindow returns the application object, and UIApplication returns nil.

So, if you recurse a view’s nextResponder until it is of type UIViewController, then you have any view’s parent viewController.

Note that it still may not have a parent view controller. But only if the view has not part of a viewController’s view’s view hierarchy.

Swift 3 and Swift 4.1 extension:

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:

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

This macro avoids category pollution:

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


@andrey answer in one line (tested in Swift 4.1):

extension UIResponder {    public var parentViewController: UIViewController? {        return next as? UIViewController ?? next?.parentViewController    }}

usage:

 let vc: UIViewController = view.parentViewController


Yes, the superview is the view that contains your view. Your view shouldn't know which exactly is its view controller, because that would break MVC principles.

The controller, on the other hand, knows which view it's responsible for (self.view = myView), and usually, this view delegates methods/events for handling to the controller.

Typically, instead of a pointer to your view, you should have a pointer to your controller, which in turn can either execute some controlling logic, or pass something to its view.