iOS-UIWindow详解

1.UIWindow简介

  1. UIWindow是一种特殊的UIView,通常在一个app中至少会有一个UIWindow。
  2. iOS程序启动完毕后,创建的第一个视图控件就是UIWindow,接着创建控制器的View,最后将控制器的View添加到UIWindow上,于是控制器的View就显示在屏幕上了。
  3. 一个iOS程序之所以能显示在屏幕上,完全是因为它有UIWindow,也就是说,没有UIWindow就看不到任何UI界面。
  4. 状态栏和键盘都是特殊的UIWindow。

那么UIWindow是如何将View显示到屏幕上的呢

这里有三个重要的对象UIScreen,UIWindow,UIView。 UIScreen对象识别物理屏幕连接到设备 UIWindow对象提供绘画支持给屏幕 UIView执行绘画,当窗口要显示内容的时候,UIView绘画出他们的内容并附加到窗口上。

这样View就显示在窗口上了

2.UIWindow的创建

1.UIWindow是什么时候创建的?

我们可以发现,当我们新建一个项目,直接在stroyboard为view设置一个背景颜色,然后运行项目,就能看到换了背景颜色的view,这说明系统已经帮我们创建了一个UIWindow,那么这个UIWindow是什么时候创建的?

我们找到程序的入口main函数,来看程序的启动过程

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

此时我们可以根据UIApplicationMain函数了解程序启动的过程

  1. 根据传递的类名创建UIApplication对象,这是第一个对象
  2. 创建UIApplication代理对象,并给UIApplicaiton对象设置代理
  3. 开启主运行循环 main events loop处理事件,保持程序一直运行
  4. 加载info.plist,判断是否指定mian(xib 或者 storyboard)如果指定就去加载

当我们把指定的Main Interface 中mian给删除的时候,重新运行程序,就会发现我们之前设置的view没有办法显示了。

Main Interface 中 Main删除

此时我们基本可以想到,UIWindow应该是在加载storyboard的时候系统创建的,那么系统是如何加载storyboard的呢? 系统在加载storyboard的时候会做以下三件事情

  1. 创建窗口
  2. 加载mian.storyboard 并实例化view controller
  3. 分配新视图控制器到窗口root viewcontroller,然后使窗口显在示屏幕上。

因此,当系统加载完info.plist,判断后发现没有main,就不会加载storyboard,也就不会帮我们创建UIWindow,那么我们需要自己在程序启动完成的时候也就是在didFinishLaunchingWithOptions方法中创建。

2.如何创建UIWindow?

首先根据系统加载storyboard时做的三件事情,我们可以总结出UIWindow创建步骤

  1. 创建窗口对象
  2. 创建窗口的根控制器,并且赋值
  3. 显示窗口

并且我们在AppDelegate.h中发现属性window

@property (strong, nonatomic) UIWindow *window;

那么我们来看一下如何创建UIWindow

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //创建窗口对象
    self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
    //创建窗口的根控制器,并且赋值
    UIViewController *rootVc = [[UIViewController alloc]init];
    self.window.rootViewController = rootVc;
    //显示窗口
    [self.window makeKeyAndVisible];
    return YES;
}

窗口显示注意点:

  1. 我们看到系统为我们创建的window属性是strong强引用,是为了不让窗口销毁,所以需要强引用
  2. 窗口的尺寸必须设置,一般设置为屏幕大小。
  3. [self.window addsubview:rootVc.view];可直接将控制器的view添加到UIWindow中,并不理会它对应的控制器,但是这种方法违背了MVC原则,当我们需要处理一些业务逻辑的时候就很麻烦了。
  4. 当发生屏幕旋转事件的时候,UIapplication对象会将旋转事件传递给UIWindow,UIWindow又会将旋转事件传递给它的根控制器,由根控制器决定是否需要旋转。UIapplication对象 -> UIWindow -> 根控制器。 ([self.window addsubview:rootVc.view];没有设置根控制器,所以不能跟着旋转)。
  5. 设置根控制器可以将对应界面的事情交给对应的控制器去管理。

那么[self.window makeKeyAndVisible];这个方法为什么就能显示窗口呢?我们来看一下[self.window makeKeyAndVisible];的底层实现了哪些功能

  1. 可以显示窗口
  2. 成为应用程序的主窗口

当我们不调用这个方法,打印self.window。

UIWindow: 0x7f920503cc80; frame = (0 0; 414 736); hidden = YES; gestureRecognizers = <NSArray: 0x7f92050332a0>; layer = <UIWindowLayer: 0x7f920503ad50>>

我们可以看到 hidden = YES;那么hidden = NO就可以显示窗口了 另外,我们在[self.window makeKeyAndVisible];前后分别输出一下application.keyWindow

  NSLog(@"%@",application.keyWindow);
  [self.window makeKeyAndVisible];
  NSLog(@"%@",application.keyWindow);

打印内容

UIWindow[6259:1268399] (null) UIWindow[6259:1268399] <UIWindow: 0x7fefdb529b30; frame = (0 0; 414 736); gestureRecognizers = <NSArray: 0x7fefdb529e40>; layer = <UIWindowLayer: 0x7fefdb529ae0>>

我们可以看到调用[self.window makeKeyAndVisible];方法之后application.keyWindow就有值了,那么[self.window makeKeyAndVisible];的底层实现就很明显了。

  1. 可以显示窗口 self.window.hidden = NO;
  2. 成为应用程序的主窗口 application.keyWindow = self.window,这个会报错,因为application.keyWindow是readonly,所以我们没有办法直接赋值。

3.通过storyboard加载控制器

刚才我们提到过系统在加载storyboard的时候会做以下三件事情

  1. 创建窗口
  2. 加载mian.storyboard 并实例化view controller
  3. 分配新视图控制器到窗口root viewcontroller,然后使窗口显在示屏幕上。

那么我们用代码来模拟实现一下

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // 1.创建窗口
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    
    // 2.加载main.storyboard,创建main.storyboard描述的控制器
    // UIStoryboard专门用来加载stroyboard
    // name:storyboard名称不需要后缀
    UIStoryboard *stroyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    
    // 加载sotryboard描述的控制器
    // 加载箭头指向的控制器
    UIViewController *vc = [stroyboard instantiateInitialViewController];
    //根据绑定标识加载 
    //UIViewController *vc = [stroyboard instantiateViewControllerWithIdentifier:@"red"];
    
    // 设置窗口的根控制器
    self.window.rootViewController = vc;
    
    // 3.显示窗口
    [self.window makeKeyAndVisible];
    
    return YES;
}

3.通过xib加载控制器

通过xib加载控制器和通过storyboard加载控制器类似,直接上代码

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    
    // 创建窗口的根控制器
    // 通过xib创建控制器
    ViewController *vc = [[ViewController alloc] initWithNibName:@"VC" bundle:nil];
  
    //vc.view.backgroundColor = [UIColor redColor];
    
    self.window.rootViewController = vc;
    
    [self.window makeKeyAndVisible];
   
    return YES;
}```

## 3.UIWindow的层级
UIWindow是有层级的,层级高的显示在最外面,当层级相同时,越靠后调用的显示在外面。

UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal; //默认,值为0 UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert; //值为2000 UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar ; // 值为1000

所以`UIWindowLevelNormal < UIWindowLevelStatusBar< UIWindowLevelAlert `
并且层级是可以做加减的`self.window.windowLevel = UIWindowLevelAlert+1;`


**关于UIApplication的介绍可以看这篇文章[iOS-UIApplication详解](http://www.jianshu.com/p/f0a2117406d8)**
`✨本文借鉴了很多前辈的文章,如果有不对的地方请指正,欢迎大家一起交流学习   xx_cc 。`

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏c#开发者

Iphone 视图跳转方法总结

1.AddInfo *control = [[AddInfo alloc] init];   [self presentModalViewController:...

3457
来自专栏君赏技术博客

【已解决】Xcode在StoryBoard设置UIView的控件类是Cocoapods的类,运行还是UIView?

ZHVerifyCodeFiled作为我写在Cocoapods的空间,现在我在测试例子的Main.storyboard直接添加一个UIView使用我们Cocoa...

1482
来自专栏xx_Cc的学习总结专栏

iOS-UIApplication详解iOS-UIApplication详解

2977
来自专栏君赏技术博客

【已解决】Xcode代码提示变量为Error Type

我之前使用Masonry布局时候经常需要设置偏移量,但是呢在设置布局哪里设置死数据不方便进行调试更换就想用一个变量。

1231
来自专栏青玉伏案

iOS开发之新浪微博山寨版代码优化

  之前发表过一篇博客“IOS开发之新浪围脖”,在编写代码的时候太偏重功能的实现了,写完基本功能后看着代码有些别扭,特别是用到的四种cell的类,重复代码有点多...

2019
来自专栏進无尽的文章

基础篇-UIApplication、UIWindow以及程序的启动流程看我就够了

这篇文章主要介绍下有关UIApplication、UIWindow以及程序的启动流程,通过这篇文章,相信你会更加理解iOS 的应用启动过程以及app应用级别的相...

1072
来自专栏Rindew的iOS技术分享

iOS初步集成极光推送后你还要做这些事

3165
来自专栏jianhuicode

React问题集序

问题描述 antd version: 2.7.4 OS and its version: windows7 Browser and its version: C...

2176
来自专栏青玉伏案

iOS开发之抽屉效果实现

  说道抽屉效果在iOS中比较有名的第三方类库就是PPRevealSideViewController。一说到第三方类库就自然而然的想到我们的CocoaPods...

2406
来自专栏iOS开发随笔

Unity项目嵌入现有iOS项目的方法

2237

扫码关注云+社区