ios - Get to UIViewController from UIView?

ID : 20222

viewed : 28

Tags : iosobjective-ccocoa-touchuiviewuiviewcontrollerios

Top 5 Answer for ios - Get to UIViewController from UIView?

vote vote

90

Using the example posted by Brock, I modified it so that it is a category of UIView instead UIViewController and made it recursive so that any subview can (hopefully) find the parent UIViewController.

@interface UIView (FindUIViewController) - (UIViewController *) firstAvailableUIViewController; @end  @implementation UIView (FindUIViewController) - (UIViewController *) firstAvailableUIViewController {     UIResponder *responder = [self nextResponder];     while (responder != nil) {         if ([responder isKindOfClass:[UIViewController class]]) {             return (UIViewController *)responder;         }         responder = [responder nextResponder];     }     return nil; }  @end 

To use this code, add it into an new class file (I named mine "UIKitCategories") and remove the class data... copy the @interface into the header, and the @implementation into the .m file. Then in your project, #import "UIKitCategories.h" and use within the UIView code:

// from a UIView subclass... returns nil if UIViewController not available UIViewController * myController = [self firstAvailableUIViewController]; 
vote vote

87

UIView is a subclass of UIResponder. UIResponder lays out the method -nextResponder with an implementation that returns nil. UIView overrides this method, as documented in UIResponder (for some reason instead of in UIView) as follows: if the view has a view controller, it is returned by -nextResponder. If there is no view controller, the method will return the superview.

Add this to your project and you're ready to roll.

@interface UIView (APIFix) - (UIViewController *)viewController; @end  @implementation UIView (APIFix)  - (UIViewController *)viewController {     if ([self.nextResponder isKindOfClass:UIViewController.class])         return (UIViewController *)self.nextResponder;     else         return nil; } @end 

Now UIView has a working method for returning the view controller.

vote vote

70

Since this has been the accepted answer for a long time, I feel I need to rectify it with a better answer.

Some comments on the need:

  • Your view should not need to access the view controller directly.
  • The view should instead be independent of the view controller, and be able to work in different contexts.
  • Should you need the view to interface in a way with the view controller, the recommended way, and what Apple does across Cocoa is to use the delegate pattern.

An example of how to implement it follows:

@protocol MyViewDelegate < NSObject >  - (void)viewActionHappened;  @end  @interface MyView : UIView  @property (nonatomic, assign) MyViewDelegate delegate;  @end  @interface MyViewController < MyViewDelegate >  @end 

The view interfaces with its delegate (as UITableView does, for instance) and it doesn't care if its implemented in the view controller or in any other class that you end up using.

My original answer follows: I don't recommend this, neither the rest of the answers where direct access to the view controller is achieved

There is no built-in way to do it. While you can get around it by adding a IBOutlet on the UIView and connecting these in Interface Builder, this is not recommended. The view should not know about the view controller. Instead, you should do as @Phil M suggests and create a protocol to be used as the delegate.

vote vote

69

I would suggest a more lightweight approach for traversing the complete responder chain without having to add a category on UIView:

@implementation MyUIViewSubclass  - (UIViewController *)viewController {     UIResponder *responder = self;     while (![responder isKindOfClass:[UIViewController class]]) {         responder = [responder nextResponder];         if (nil == responder) {             break;         }     }     return (UIViewController *)responder; }  @end 
vote vote

52

Combining several already given answers, I'm shipping on it as well with my implementation:

@implementation UIView (AppNameAdditions)  - (UIViewController *)appName_viewController {     /// Finds the view's view controller.      // Take the view controller class object here and avoid sending the same message iteratively unnecessarily.     Class vcc = [UIViewController class];      // Traverse responder chain. Return first found view controller, which will be the view's view controller.     UIResponder *responder = self;     while ((responder = [responder nextResponder]))         if ([responder isKindOfClass: vcc])             return (UIViewController *)responder;      // If the view controller isn't found, return nil.     return nil; }  @end 

The category is part of my ARC-enabled static library that I ship on every application I create. It's been tested several times and I didn't find any problems or leaks.

P.S.: You don't need to use a category like I did if the concerned view is a subclass of yours. In the latter case, just put the method in your subclass and you're good to go.

Top 3 video Explaining ios - Get to UIViewController from UIView?

Related QUESTION?