ios 给定一个视图,我如何获得它的 viewController?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1372977/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 16:22:51  来源:igfitidea点击:

Given a view, how do I get its viewController?

iosiphonedebugginguiviewuiviewcontroller

提问by mahboudz

I have a pointer to a UIView. How do I access its UIViewController? [self superview]is another UIView, but not the UIViewController, right?

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

采纳答案by Dimitar Dimitrov

Yes, the superviewis the view that contains your view. Your view shouldn't know which exactly is its view controller, because that would break MVC principles.

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

The controller, on the other hand, knows which view it's responsible for (self.view = myView), and usually, this view delegates methods/events for handling to the controller.

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

Typically, instead of a pointer to your view, you should have a pointer to your controller, which in turn can either execute some controlling logic, or pass something to its view.

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

回答by mxcl

From the UIResponderdocumentation for nextResponder:

UIResponder文档中nextResponder

The UIResponder class does not store or set the next responder automatically, instead returning nil by default. Subclasses must override this method to set the next responder. UIView implements this method by returning the UIViewController object that manages it (if it has one) or its superview (if it doesn't); UIViewController implements the method by returning its view's superview; UIWindow returns the application object, and UIApplication returns nil.

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

So, if you recurse a view's nextResponderuntil it is of type UIViewController, then you have any view's parent viewController.

所以,如果你递归一个视图nextResponder直到它是 type UIViewController,那么你有任何视图的父视图控制器。

Note that it still may nothave a parent view controller. But only if the view has not part of a viewController's view's view hierarchy.

请注意,它可能仍然没有父视图控制器。但前提是该视图不属于 viewController 视图的视图层次结构。

Swift 3 and Swift 4.1extension:

Swift 3 和Swift 4.1扩展:

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

Swift 2 extension:

斯威夫特 2 扩展:

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

Objective-C category:

Objective-C 类别:

@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

This macro avoids category pollution:

这个宏避免了类别污染:

#define UIViewParentController(__view) ({ \
    UIResponder *__responder = __view; \
    while ([__responder isKindOfClass:[UIView class]]) \
        __responder = [__responder nextResponder]; \
    (UIViewController *)__responder; \
})

回答by Federico Zanetello

@andreyanswer in one line (tested in Swift 4.1):

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

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

usage:

用法:

 let vc: UIViewController = view.parentViewController

回答by Leo Natan

For debug purposes only, you can call _viewDelegateon views to get their view controllers. This is private API, so not safe for App Store, but for debugging it is useful.

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

Other useful methods:

其他有用的方法:

  • _viewControllerForAncestor- get the first controller that manages a view in the superview chain. (thanks n00neimp0rtant)
  • _rootAncestorViewController- get the ancestor controller whose view hierarchy is set in the window currently.
  • _viewControllerForAncestor- 获取第一个管理超级视图链中视图的控制器。(感谢 n00neimp0rtant)
  • _rootAncestorViewController- 获取当前窗口中设置了视图层次结构的祖先控制器。

回答by Andrey

To get reference to UIViewController having UIView, you could make extension of UIResponder (which is super class for UIView and UIViewController), which allows to go up through the responder chain and thus reaching UIViewController (otherwise returning nil).

要获得对具有 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

回答by iTSangar

The fast and generic way in Swift 3:

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)
}

回答by m4js7er

If you are not familiar with the code and you want to find ViewController coresponding to given view, then you can try:

如果你不熟悉代码,想找到对应给定视图的ViewController,那么你可以尝试:

  1. Run app in debug
  2. Navigate to screen
  3. Start View inspector
  4. Grab the View you want to find (or a child view even better)
  5. From the right pane get the address (e.g. 0x7fe523bd3000)
  6. In debug console start writing commands:
  1. 在调试中运行应用程序
  2. 导航到屏幕
  3. 启动视图检查器
  4. 抓取您想要查找的视图(或者更好的子视图)
  5. 从右侧窗格中获取地址(例如 0x7fe523bd3000)
  6. 在调试控制台中开始编写命令:
    po (UIView *)0x7fe523bd3000
    po [(UIView *)0x7fe523bd3000 nextResponder]
    po [[(UIView *)0x7fe523bd3000 nextResponder] nextResponder]
    po [[[(UIView *)0x7fe523bd3000 nextResponder] nextResponder] nextResponder]
    ...

In most cases you will get UIView, but from time to time there will be UIViewController based class.

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

回答by Nava Carmon

I think you can propagate the tap to the view controller and let it handle it. This is more acceptable approach. As for accessing a view controller from its view, you should maintain a reference to a view controller, since there is no another way. See this thread, it might help: Accessing view controller from a view

我认为您可以将点击传播到视图控制器并让它处理它。这是更容易接受的方法。至于从视图访问视图控制器,您应该维护对视图控制器的引用,因为没有其他方法。请参阅此线程,它可能会有所帮助: 从视图访问视图控制器

回答by Denis Krivitski

More type safe code for Swift 3.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
    }
}

回答by Morten Carlsen

Alas, this is impossible unless you subclass the view and provide it with an instance property or alike which stores the view controller reference inside it once the view is added to the scene...

唉,这是不可能的,除非您对视图进行子类化并为其提供实例属性或类似属性,一旦将视图添加到场景中,该属性将视图控制器引用存储在其中...

In most cases - it is very easy to work around the original problem of this post as most view controllers are well-known entities to the programmer who was responsible for adding any subviews to the ViewController's View ;-) Which is why I guess that Apple never bothered to add that property.

在大多数情况下 - 很容易解决这篇文章的原始问题,因为大多数视图控制器对于负责向 ViewController 的视图添加任何子视图的程序员来说都是众所周知的实体 ;-) 这就是为什么我猜苹果从不费心添加该属性。