给定一个视图,我如何获取它的视图控制器?

10 浏览
0 Comments

给定一个视图,我如何获取它的视图控制器?

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

0
0 Comments

问题的出现原因是根据MVC原则,一个view不应该知道它所属的viewController,因为这样会破坏MVC的原则。但是,有时候我们需要通过一个view来获取它所属的viewController,以便传递一些消息给它。

解决方法是,我们应该在view中保持一个指向它所属的viewController的指针,而不是直接知道它所属的viewController。这样,view可以通过这个指针来调用viewController的方法或者传递消息给它。

虽然有人可能会认为这样做会破坏MVC的原则,因为一个view只应该关注于展示数据,而不需要知道它所属的viewController。但是,实际上,获取一个view所属的viewController是一个自然而然的需求,并且也有一些方法可以实现这个目标。

根据MVC原则,一个view不应该直接知道它所属的viewController,但是有时候我们需要通过一个view来获取它所属的viewController,以便传递消息给它。为了解决这个问题,我们可以在view中保持一个指向它所属的viewController的指针,以便能够方便地获取到viewController并进行操作。

0
0 Comments

问题出现的原因是,当我们有一个视图(view)时,我们想要获取它所属的视图控制器(viewController),但是在UIKit中并没有提供直接获取视图控制器的方法。因此,我们需要找到一种解决方法来获取视图所属的视图控制器。

解决方法是通过扩展(extension)UIResponder类来添加一个parentViewController属性。这个属性会尝试通过next属性(指向下一个响应者)来获取视图控制器,如果next属性指向的是一个UIViewController对象,则直接返回该对象,否则递归地调用parentViewController属性,直到找到视图控制器为止。

在Swift 4.1中,我们可以使用以下代码来实现这个扩展:

extension UIResponder {

public var parentViewController: UIViewController? {

return next as? UIViewController ?? next?.parentViewController

}

}

使用时,只需要调用视图的parentViewController属性即可获取其所属的视图控制器:

let vc: UIViewController = view.parentViewController

需要注意的是,如果在与UIView相同的文件中定义这个扩展,parentViewController属性不能定义为public,可以使用fileprivate修饰符。如果不这样做,虽然代码可以编译通过,但是无法正常工作。

在Xamarin中,可以使用以下代码来实现这个扩展:

public static class Extensions
{
    public static UIViewController ParentViewController(this UIResponder view)
    {
        return (view.NextResponder as UIViewController) ?? view.NextResponder?.ParentViewController();
    }
}

0
0 Comments

给定一个视图,如何获取它的viewController?

在UIResponder文档中,关于nextResponder的说明如下:

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

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

请注意,它可能仍然没有父视图控制器。但只有当视图不属于viewController的视图层次结构时才会如此。

Swift 3和Swift 4.1扩展:

extension UIView {

var parentViewController: UIViewController? {

// 从next开始(因为我们知道self不是一个UIViewController)。

var parentResponder: UIResponder? = self.next

while parentResponder != nil {

if let viewController = parentResponder as? UIViewController {

return viewController

}

parentResponder = parentResponder?.next

}

return nil

}

}

Swift 2扩展:

extension UIView {

var parentViewController: UIViewController? {

var parentResponder: UIResponder? = self.nextResponder()

while parentResponder != nil {

if let viewController = parentResponder as? UIViewController {

return viewController

}

parentResponder = parentResponder!.nextResponder()

}

return nil

}

}

Objective-C分类:

UIView (mxcl)
- (UIViewController *)parentViewController;
 UIView (mxcl)
- (UIViewController *)parentViewController {
    UIResponder *responder = [self nextResponder];
    while (responder != nil) {
        if ([responder isKindOfClass:[UIViewController class]]) {
            return (UIViewController *)responder;
        }
        responder = [responder nextResponder];
    }
    return nil;
}

这个宏避免了类别污染:

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

只有一点需要注意:如果你担心类别污染,只需将其定义为静态函数而不是宏。此外,强制类型转换是危险的,此外,宏可能不正确,但我不确定。

这个宏是有效的,我个人只使用宏版本。我开始了很多项目,有一个我随处放置的头文件,里面有一堆这样的宏实用程序函数。这样可以节省时间。如果你不喜欢宏,你可以将其改写为一个函数,但是静态函数似乎很麻烦,因为你必须在想要使用它的每个文件中都放置一个。似乎更好的方法是在头文件中声明一个非静态函数,在某个地方的.m文件中定义它。

很高兴避免一些委托或通知。谢谢!

你应该将其作为UIResponder的扩展;)。非常有启发的帖子。

0