Is there a built-in way to get from a UIView to its UIViewController? I know you can get from UIViewController to its UIView via [self view] but I was wondering if there is a reverse reference?
There is no built-in way to do it. I did it by adding a IBOutlet
on the UIView
and connecting these in Interface Builder.
I think you should ask yourself why do you need this, and if the View Controller can't do the job instead. (This was my experience)
There is no way.
What I do is pass the UIViewController pointer to the UIView (or an appropriate inheritance). I'm sorry I can't help with the IB approach to the problem because I don't believe in IB.
To answer the first commenter: sometimes you do need to know who called you because it determines what you can do. For example with a database you might have read access only or read/write ...
Even though this can technically be solved as pgb recommends, IMHO, this is a design flaw. The view should not need to be aware of the controller.
I have a similar situation where I have a EAGLView under a UIViewController which again is under a UINavigationController. What I want to do is to navigate to a new view when there's a certain touch event in the opengl view. Since the touch is handled by the EAGLView, how do I tell the navigation view controller on the top to change the view?
It would probably be considered kind of bogus (hey, I just started looking at iPhone in Dec!) but I had a similar situation as mayoneez (I wanted to switch views in response to a gesture in an EAGLView), and I got the EAGL's view controller this way:
EAGLViewController vc = ((EAGLAppDelegate)[[UIApplication sharedApplication] delegate]).viewController;
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 UIViewController (FixedApi)
- (UIViewController *)viewController;
@end
@implementation UIViewController (FixedApi)
- (UIViewController *)viewController;
{
id nextResponder = [self nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return nextResponder;
} else {
return nil;
}
}
@end
Now UIView has a working method for returning the view controller.
Using the example posted by Brock, I modified it so that its 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;
- (id) traverseResponderChainForUIViewController;
@end
@implementation UIView (FindUIViewController)
- (UIViewController *) firstAvailableUIViewController {
// convenience function for casting and to "mask" the recursive function
return (UIViewController *)[self traverseResponderChainForUIViewController];
}
- (id) traverseResponderChainForUIViewController {
id nextResponder = [self nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return nextResponder;
} else if ([nextResponder isKindOfClass:[UIView class]]) {
return [nextResponder traverseResponderChainForUIViewController];
} else {
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];