views:

3906

answers:

8

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?

A: 

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)

pgb
That's very bad advice. You shouldn't reference a view controller from a view
Philippe Leybaert
+1  A: 

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 ...

John Smith
What does that mean - "I don't believe in IB" ? I've launched it, it certainly exists.
marcc
You need a better grasp of fun and abstraction, especially with regards to the English language. It means I don't like it.
John Smith
+1  A: 

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.

Ushox
I'm just not sure how the viewController can be told that one of its view's is going away and it needs to call one of its viewXXXAppear/viewXXXDisappear methods.
mahboudz
This is the idea behind the Observer pattern. The Observed (the View in this case) should not be aware of its Observers directly. The Observer should only receive the callbacks that they're interested in.
Ushox
A: 

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?

+1  A: 

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;

gulchrider
Please rewrite your code as follows: `EAGLViewController *vc = [(EAGLAppDelegate *)[UIApplication sharedApplication].delegate viewController];`.
Jonathan Sterling
A: 

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.

Brock
A: 

Does anyone know how to use the above code?

franccjk
+3  A: 

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];
Phil M
And one reason you need to allow the UIView to be aware of its UIViewController is when you have custom UIView subclasses that need to push a modal view/dialog.
Phil M