Is there a built-in way to get from a UIView
to its UIViewController
? I know you can get from UIViewController
to its UIView
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.
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:
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.
Maybe I'm late here. But in this situation I don't like category (pollution). I love this way:
#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})
To get the controller of a given view, one can use UIFirstResponder chain.
customView.target(forAction: Selector("viewDidLoad"), withSender: nil)
Swiftier solution
extension UIView {
var parentViewController: UIViewController? {
for responder in sequence(first: self, next: { $0.next }) {
if let viewController = responder as? UIViewController {
return viewController
}
}
return nil
}
}
To Phil's answer:
In line: id nextResponder = [self nextResponder];
if self(UIView) is not a subview of ViewController's view, if you know hierarchy of self(UIView) you can use also: id nextResponder = [[self superview] nextResponder];
...