iOS视图的层级结构

前言

看这篇博客之前,先安利一片文章《View Debugging in Xcode 6》,英文能力有限也没关系,译文:《详解Xcode 6的视图调试》

一句代码引起了我的注意

今天在看到别人的代码的时候,发现了下面这样一句代码:

1
[[UIApplication sharedApplication].delegate.window addSubview:self.shareView];

在 UIWindow 上面去添加视图(self.shareView)。

自己写代码的习惯是会写成下面这种形式:

1
[[UIApplication sharedApplication].delegate.window.rootViewController.view addSubview:self.shareView];

添加到根控制器(rootViewController)的视图也是可以显示,和上面的代码有一样的效果,那他们究竟有什么样的区别呢?

视图层级

添加到window与添加到rootViewController.view的区别

我们将两个相同的 UIImageView 添加到 windowrootViewController.view(rootViewController为UINavigationController),push 一个新的 UIViewController 后,在 viewDidLoad 方法下运行:

1
2
3
4
5
6
7
8
9
10
11
UIWindow *window = [UIApplication sharedApplication].delegate.window;
UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
UIImageView *imageView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"IMG_3566.PNG"]];
[imageView1 setFrame:CGRectMake(0, 70, 300, 200)];

UIImageView *imageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"IMG_3566.PNG"]];
[imageView2 setFrame:CGRectMake(0, 300, 300, 200)];


[window addSubview:imageView1];
[rootViewController.view addSubview:imageView2];

会有如下的效果:

乍一看没有什么区别?如果没有区别,我写这博客干嘛呢,是吧?哈哈哈~

通过Xcode来观察他们的视图层级结构

xcode 工作区点击以下这两步:

可以看到如下的视图层级图:

左侧栏可以看出其层级结构:

添加到 rootViewController.viewUIImageView 被添加到 UILayoutContainerView 底下。

添加到 UIWindowUIImageView 被添加到 UIWindow 底下。

表象上看,两个 UIImageView 都是显示在视图的顶端,但是实际上层级结构不一样

各个视图层级的作用

UINavigationController 为 rootViewController

我们看下面的这张图是:

  1. UILayoutContainerView:最底层包含所有视图的容器。

  2. UINavigationTransitionView:导航控制器在这里发生转场行为的容器视图。

  3. UIViewControllerWrapperView: 包含 viewController 的 view属性的封装视图。

  4. UIView: viewController 的最上层视图 (与viewController的view 属性一致)

要记得,当我们通过 [UIApplication sharedApplication].delegate.window.rootViewController.view 去获取一个 View,即使 UINavigationController 没有push任何视图,你拿到还是最底层的 UILayoutContainerView。而不是 顶层的 UIView

UITabBarController 为 rootViewController

UIViewController 为 rootViewController

回到最初的起点

我们回到刚刚上面的两句代码,其实这两句代码都是有点小“问题”的:

问题1

1
[[UIApplication sharedApplication].delegate.window addSubview:self.shareView];

不能使用于 rootViewControllerviewDidLoad 方法中。会出现以下这种情况:

rootViewController 还没有加载完毕时,已经将视图添加到 UIWindow 中了,后面会因为 rootViewController 的加载而挡住原来的视图。

问题2

1
[[UIApplication sharedApplication].delegate.window.rootViewController.view addSubview:self.shareView];

在模态视图下不可使用,模态视图的出现会,销毁之前的我们 rootViewController.view,导致我们只能看到一闪而过的效果,view 不能长久的停留在屏幕上。

问题3

stackoverflow,当别人提起 UILayoutContainerView 时,很多的建议都是不要在上面做操作,因为他并非官方开放给开发者使用的接口。

所以我们还是避免使用:

1
[[UIApplication sharedApplication].delegate.window.rootViewController.view addSubview:self.shareView];

自己要改掉该习惯。

最后的话

关于 iOS 的视图层级,如果要更深入的去研究的话,还是推荐去官方文档翻一翻,可能显示的开发会跟这一块打交道的机会不是很多,但是要真正深入的去了解iOS的视图机制,还是属于要跨过去的坎。