Get to UIViewController from UIView?

前端 未结 29 1733
一整个雨季
一整个雨季 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:32

    I modified de answer so I can pass any view, button, label etc. to get it's parent UIViewController. Here is my code.

    +(UIViewController *)viewController:(id)view {
        UIResponder *responder = view;
        while (![responder isKindOfClass:[UIViewController class]]) {
            responder = [responder nextResponder];
            if (nil == responder) {
                break;
            }
        }
        return (UIViewController *)responder;
    }
    

    Edit Swift 3 Version

    class func viewController(_ view: UIView) -> UIViewController {
            var responder: UIResponder? = view
            while !(responder is UIViewController) {
                responder = responder?.next
                if nil == responder {
                    break
                }
            }
            return (responder as? UIViewController)!
        }
    

    Edit 2:- Swift Extention

    extension UIView
    {
        //Get Parent View Controller from any view
        func parentViewController() -> UIViewController {
            var responder: UIResponder? = self
            while !(responder is UIViewController) {
                responder = responder?.next
                if nil == responder {
                    break
                }
            }
            return (responder as? UIViewController)!
        }
    }
    
    0 讨论(0)
  • 2020-11-22 17:32

    Updated version for swift 4 : Thanks for @Phil_M and @paul-slm

    static func firstAvailableUIViewController(fromResponder responder: UIResponder) -> UIViewController? {
        func traverseResponderChainForUIViewController(responder: UIResponder) -> UIViewController? {
            if let nextResponder = responder.next {
                if let nextResp = nextResponder as? UIViewController {
                    return nextResp
                } else {
                    return traverseResponderChainForUIViewController(responder: nextResponder)
                }
            }
            return nil
        }
    
        return traverseResponderChainForUIViewController(responder: responder)
    }
    
    0 讨论(0)
  • 2020-11-22 17:32

    Two solutions as of Swift 5.2:

    • More on the functional side
    • No need for the return keyword now
    0 讨论(0)
  • 2020-11-22 17:33

    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.

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

    It's surely a bad idea and a wrong design, but I'm sure we can all enjoy a Swift solution of the best answer proposed by @Phil_M:

    static func firstAvailableUIViewController(fromResponder responder: UIResponder) -> UIViewController? {
        func traverseResponderChainForUIViewController(responder: UIResponder) -> UIViewController? {
            if let nextResponder = responder.nextResponder() {
                if let nextResp = nextResponder as? UIViewController {
                    return nextResp
                } else {
                    return traverseResponderChainForUIViewController(nextResponder)
                }
            }
            return nil
        }
    
        return traverseResponderChainForUIViewController(responder)
    }
    

    If your intention is to do simple things, as showing a modal dialog or tracking data, that doesn't justify the use of a protocol. I personally store this function in an utility object, you can use it from anything that implement the UIResponder protocol as:

    if let viewController = MyUtilityClass.firstAvailableUIViewController(self) {}
    

    All credit to @Phil_M

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

    I think there is a case when the observed needs to inform the observer.

    I see a similar problem where the UIView in a UIViewController is responding to a situation and it needs to first tell its parent view controller to hide the back button and then upon completion tell the parent view controller that it needs to pop itself off the stack.

    I have been trying this with delegates with no success.

    I don't understand why this should be a bad idea?

    0 讨论(0)
提交回复
热议问题