鉴于一个观点,我如何获得它的viewController?

时间:2009-09-03 11:55:35

标签: ios iphone debugging uiview uiviewcontroller

我有一个指向UIView的指针。如何访问其UIViewController[self superview]是另一个UIView,但不是UIViewController,对吗?

12 个答案:

答案 0 :(得分:241)

来自UIResponder的{​​{1}}文档:

  

UIResponder类不会自动存储或设置下一个响应者,而是默认返回nil。子类必须覆盖此方法以设置下一个响应者。 UIView通过返回管理它的UIViewController对象(如果有的话)或超级视图(如果没有)来实现此方法; UIViewController通过返回其视图的superview来实现该方法; UIWindow返回应用程序对象,UIApplication返回nil。

因此,如果你递归一个视图nextResponder直到它是nextResponder类型,那么你有任何视图的父viewController。

请注意,它仍然可能具有父视图控制器。但仅当视图不是viewController视图的视图层次结构的一部分时。

Swift 3和 Swift 4.1 扩展程序:

UIViewController

Swift 2扩展名:

extension UIView {
    var parentViewController: UIViewController? {
        var parentResponder: UIResponder? = self
        while parentResponder != nil {
            parentResponder = parentResponder!.next
            if let viewController = parentResponder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
}

Objective-C类别:

extension UIView {
    var parentViewController: UIViewController? {
        var parentResponder: UIResponder? = self
        while parentResponder != nil {
            parentResponder = parentResponder!.nextResponder()
            if let viewController = parentResponder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
}

这个宏避免了类别污染:

@interface UIView (mxcl)
- (UIViewController *)parentViewController;
@end

@implementation UIView (mxcl)
- (UIViewController *)parentViewController {
    UIResponder *responder = self;
    while ([responder isKindOfClass:[UIView class]])
        responder = [responder nextResponder];
    return (UIViewController *)responder;
}
@end

答案 1 :(得分:38)

是的,superview是包含您的观点的视图。您的视图不应该知道它的视图控制器究竟是哪个,因为这会破坏MVC原则。

另一方面,控制器知道它负责哪个视图(self.view = myView),并且通常,此视图将处理的方法/事件委托给控制器。

通常,您应该有一个指向控制器的指针,而不是指向视图的指针,而控制器又可以执行某些控制逻辑,或者将某些内容传递给它的视图。

答案 2 :(得分:21)

仅出于调试目的,您可以在视图上调用_viewDelegate以获取其视图控制器。这是私有API,因此对App Store不安全,但对于调试它很有用。

其他有用的方法:

  • _viewControllerForAncestor - 获取管理的第一个控制器 超视链中的视图。 (感谢n00neimp0rtant)
  • _rootAncestorViewController - 获取其祖先控制器 视图层次结构目前在窗口中设置。

答案 3 :(得分:20)

@andrey 在一行中回答(在 Swift 4.1 中测试):

extension UIResponder {
    public var parentViewController: UIViewController? {
        return next as? UIViewController ?? next?.parentViewController
    }
}

用法:

 let vc: UIViewController = view.parentViewController

答案 4 :(得分:9)

要获得对具有UIView的UIViewController的引用,你可以扩展UIResponder(它是UIView和UIViewController的超类),它允许通过响应器链并因此到达UIViewController(否则返回nil)。

extension UIResponder {
    func getParentViewController() -> UIViewController? {
        if self.nextResponder() is UIViewController {
            return self.nextResponder() as? UIViewController
        } else {
            if self.nextResponder() != nil {
                return (self.nextResponder()!).getParentViewController()
            }
            else {return nil}
        }
    }
}

//Swift 3
extension UIResponder {
    func getParentViewController() -> UIViewController? {
        if self.next is UIViewController {
            return self.next as? UIViewController
        } else {
            if self.next != nil {
                return (self.next!).getParentViewController()
            }
            else {return nil}
        }
    }
}

let vc = UIViewController()
let view = UIView()
vc.view.addSubview(view)
view.getParentViewController() //provide reference to vc

答案 5 :(得分:4)

Swift 3中的快速通用方式:

extension UIResponder {
    func parentController<T: UIViewController>(of type: T.Type) -> T? {
        guard let next = self.next else {
            return nil
        }
        return (next as? T) ?? next.parentController(of: T.self)
    }
}

//Use:
class MyView: UIView {
    ...
    let parentController = self.parentController(of: MyViewController.self)
}

答案 6 :(得分:1)

如果您不熟悉代码并且想要找到与给定视图相对应的ViewController,那么您可以尝试:

  1. 在调试中运行app
  2. 导航至屏幕
  3. 启动查看检查器
  4. 抓住您想要查找的视图(或更好地查看子视图)
  5. 从右侧窗格中获取地址(例如0x7fe523bd3000)
  6. 在调试控制台中开始编写命令:
  7.     po (UIView *)0x7fe523bd3000
        po [(UIView *)0x7fe523bd3000 nextResponder]
        po [[(UIView *)0x7fe523bd3000 nextResponder] nextResponder]
        po [[[(UIView *)0x7fe523bd3000 nextResponder] nextResponder] nextResponder]
        ...
    

    在大多数情况下,您将获得UIView,但有时会有基于UIViewController的类。

答案 7 :(得分:0)

我认为您可以将水龙头传播到视图控制器并让它处理它。这是更可接受的方法。至于从视图中访问视图控制器,您应该维护对视图控制器的引用,因为没有其他方法。看到这个帖子,它可能会有所帮助: Accessing view controller from a view

答案 8 :(得分:0)

Swift 3.0的更多类型安全代码

extension UIResponder {
    func owningViewController() -> UIViewController? {
        var nextResponser = self
        while let next = nextResponser.next {
            nextResponser = next
            if let vc = nextResponser as? UIViewController {
                return vc
            }
        }
        return nil
    }
}

答案 9 :(得分:0)

唉,这是不可能的,除非你继承视图并为它提供一个实例属性或类似的东西,一旦将视图添加到场景中,它就会将视图控制器引用存储在其中......

在大多数情况下 - 解决这篇文章的原始问题非常容易,因为大多数视图控制器都是程序员的知名实体,负责将任何子视图添加到ViewController的视图中;-)这就是为什么我认为Apple从不打算添加该属性。

答案 10 :(得分:0)

有点晚了,但这是一个扩展程序,可以让你找到任何类型的响应者,包括ViewController。

extension NSObject{
func findNext(type: AnyClass) -> Any{
    var resp = self as! UIResponder

    while !resp.isKind(of: type.self) && resp.next != nil
    {
        resp = resp.next!
    }

    return resp
  }                       
}

答案 11 :(得分:-1)

如果设置断点,可以将其粘贴到调试器中以打印视图层次结构:

po [[UIWindow keyWindow] recursiveDescription]

你应该能够在那个混乱的地方找到你的观点的父母:)