首页 iOS 如何在视图内获取父视图的视图控制器?
文章
取消

iOS 如何在视图内获取父视图的视图控制器?

关于 如何在视图内获取父视图的视图控制器 网上有很多类似主题的文章, 但是方法比较麻烦, 并且扩展性比较差. 于是自己总结一个易于扩展的方法.

UIResponder

UIResponder 主要职责是 iOS 中专门用来响应用户操作. 我们常用的 UI 相关的类有很大一部分都继承自它.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
NSObject 
   |- UIResponder 
           |- UIApplication             
           |- UIView 
           |     |- UIWebView           
           |     |- UIControl 
           |            |- UIButton
           |            |- UIDatePicker
           |            |- UIPageControl
           |            |- UISegmentControl
           |            |- UITextField
           |            |- UISlider
           |            |- UISwitch
           |- UIViewController 
                     |- UISplitViewController
                     |- UITabBarController
                     |- UITableViewController
                     |- UINavigationViewController
                                 |- UIImagePickerViewController
                                 |- UIVideoEditorController

继承链上的每一个类都是我们非常熟悉的. 关于 响应链 此处不做过多讲解, 后面另开新坑详细说. UIResponder 有这样一个属性:

1
2
3
// Returns the next responder in the responder chain, or nil if there is no next responder.
// 返回当前对象在响应链中的下一响应对象
@property(nonatomic, readonly, nullable) UIResponder *nextResponder;

在 UI 层级中我们的 UI 层级之间究竟有什么样的关系? 首先创建如下 UI 作为测试工程:

UI 层级关系是:

1
2
3
4
5
UITabBarController(Root) 
  |- UINavigationController 
     |- UIViewController 
        |- UIViewController.view 
           |- CustomView

我们可以通过下面一段代码来验证(CustomView):

1
2
3
4
5
6
7
8
9
10
11
@implementation CustomView

- (void)didMoveToWindow {
    UIResponder *responder = self;
    while (responder != nil) {
        NSLog(@"%@", [responder class]);
        responder = [responder nextResponder];
    }
}

@end

打印结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CustomView
UIView
ViewController
UIViewControllerWrapperView
UINavigationTransitionView
UILayoutContainerView
UINavigationController
UIViewControllerWrapperView
UITransitionView
UILayoutContainerView
UITabBarController
UIWindow
UIApplication
AppDelegate

以上就是图片中 UI 层级中 响应链 中的对象由上往下类的列表.

获取响应链层级中的 UIViewController

回归正题, 本文主要通过响应链来获取 UI 层级中某层对象. 就如 CustomView 中实现的代码部分一样我们可以通过循环遍历 [responder nextResponder] 来获取某一层级中的对象. 但是这种方法泛用性太差, 我们来找一个泛用性更高的方法.

正如上文所述, 我们常用的 UI 控件/对象 一大部分都继承自 UIResponder, 并且 nextResponder 属性也继承自 UIResponder, 我们就通过给 UIResponder 添加 Category 的方法来实现我们的目的.

创建 Category

获取 CustomView 父视图的视图控制器:

1
2
3
4
5
6
7
8
9
10
@implementation UIResponder (Category)

- (UIViewController *)nextViewController {
    if ([self isKindOfClass:[UIViewController class]]) {
        return (UIViewController *)self;
    }
    return [[self nextResponder] nextViewController];
}

@end

这样就达成了我们标题的目的, 但是泛用性方面太差. 我们对方法进行修改:

1
2
3
4
5
6
7
8
9
10
11
12
/**
 获取响应链中指定类的实例对象

 @param class 筛选类
 @return 实例对象
 */
- (UIResponder *)nextResponder:(Class)class {
    if ([self isKindOfClass:class]) {
        return self;
    }
    return [self nextResponder:class];
}

当然, 这个方法也有其缺陷, 比如: 如果我们的 响应链 层级中有 nCustomViewController 的实例对象, 我们就只能获取到最高层的那个, 而无法获取更加底层的实例对象. 当然, 你也可以通过 做标记 等多种方法来实现, 自由度非常高, 完全由你自己把控.

本文由作者按照 CC BY 4.0 进行授权

iOS 数组常用操作: 排序

iOS 打包上传卡在: Authenticating with the iTunes store