UIWindow详解

前言

最近在做一个通知弹框的需求,应用到了UIWindow,之前没有研究过,趁着这次机会了解下UIWindow。简书上发现一篇好文章,特转载过来,感谢作者。
作者:xx_cc链接:http://www.jianshu.com/p/af2a6a438a0a
应用内通知的一个demo: https://github.com/terryworona/TWMessageBarManager

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

4.通过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详解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容