Get to UIViewController from UIView?

2018-12-31 18:51发布

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?

25条回答
路过你的时光
2楼-- · 2018-12-31 18:55

While these answers are technically correct, including Ushox, I think the approved way is to implement a new protocol or re-use an existing one. A protocol insulates the observer from the observed, sort of like putting a mail slot in between them. In effect, that is what Gabriel does via the pushViewController method invocation; the view "knows" that it is proper protocol to politely ask your navigationController to push a view, since the viewController conforms to the navigationController protocol. While you can create your own protocol, just using Gabriel's example and re-using the UINavigationController protocol is just fine.

查看更多
浮光初槿花落
3楼-- · 2018-12-31 18:56

The simplest do while loop for finding the viewController.

-(UIViewController*)viewController
{
    UIResponder *nextResponder =  self;

    do
    {
        nextResponder = [nextResponder nextResponder];

        if ([nextResponder isKindOfClass:[UIViewController class]])
            return (UIViewController*)nextResponder;

    } while (nextResponder != nil);

    return nil;
}
查看更多
路过你的时光
4楼-- · 2018-12-31 18:56

Swift 4

(more concise than the other answers)

fileprivate extension UIView {

  var firstViewController: UIViewController? {
    let firstViewController = sequence(first: self, next: { $0.next }).first(where: { $0 is UIViewController })
    return firstViewController as? UIViewController
  }

}

My use case for which I need to access the view first UIViewController: I have an object that wraps around AVPlayer / AVPlayerViewController and I want to provide a simple show(in view: UIView) method that will embed AVPlayerViewController into view. For that, I need to access view's UIViewController.

查看更多
残风、尘缘若梦
5楼-- · 2018-12-31 18:59

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.

查看更多
永恒的永恒
6楼-- · 2018-12-31 18:59

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.

查看更多
有味是清欢
7楼-- · 2018-12-31 19:01

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.

查看更多
登录 后发表回答