Get to UIViewController from UIView?

前端 未结 29 1732
一整个雨季
一整个雨季 2020-11-22 16:57

Is there a built-in way to get from a UIView to its UIViewController? I know you can get from UIViewController to its UIView

相关标签:
29条回答
  • 2020-11-22 17:44

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

    0 讨论(0)
  • 2020-11-22 17:45

    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.

    0 讨论(0)
  • 2020-11-22 17:45
    var parentViewController: UIViewController? {
        let s = sequence(first: self) { $0.next }
        return s.compactMap { $0 as? UIViewController }.first
    }
    
    0 讨论(0)
  • 2020-11-22 17:46

    Don't forget that you can get access to the root view controller for the window that the view is a subview of. From there, if you are e.g. using a navigation view controller and want to push a new view onto it:

        [[[[self window] rootViewController] navigationController] pushViewController:newController animated:YES];
    

    You will need to set up the rootViewController property of the window properly first, however. Do this when you first create the controller e.g. in your app delegate:

    -(void) applicationDidFinishLaunching:(UIApplication *)application {
        window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        RootViewController *controller = [[YourRootViewController] alloc] init];
        [window setRootViewController: controller];
        navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
        [controller release];
        [window addSubview:[[self navigationController] view]];
        [window makeKeyAndVisible];
    }
    
    0 讨论(0)
  • 2020-11-22 17:48

    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;
    - (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];
    
    0 讨论(0)
提交回复
热议问题