写在前面

本文内容绝大部分都参考唐巧大神的《iOS开发进阶》,只是结合不是特别长的开发经验加以补充;最后基于UIWindow自定义了一个类似于微信的ActionSheet。

UIWindow简介

在iOS App中,UIWindow是最顶层的界面内容,我们使用UIWindow和UIView来呈现界面。UIWindow并不包含任何默认的内容,但是它被当作UIView的容器,用于放置应用中所有的UIView。

从继承关系来看,UIWindow继承自UIView,所以UIWindow除了具有UIView的所有功能之外,还增加了一些特有的属性和方法,而我们最常用的方法,就是在App刚启动时,调用UIWindow的makeKeyAndVisible方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
 
MainNavigationController *VC = [MainNavigationController sharedMainNavigationController];
 
self.window.rootViewController = VC;
[self.window makeKeyAndVisible];
 
return YES;
}

P.S:makeKeyAndVisible方法,从方法名字面上看有两层意思:让window成为key window,使得window可见。

总的来看,UIWindow的主要作用有:

  1. 作为UIView的最顶层容器,包含应用显示所有的UIView;
  2. 传递触摸消息和键盘事件给UIView;

为UIWindow增加UIView

通常我们有两种办法给UIWindow增加子UIView:

  1. 通过调用addSubView方法,因为UIWindow是UIView的子类,所以它可以使用UIView的addSubView方法给自己增加子UIView,从而承担容器的作用;
  2. 通过设置其特有的rootViewController属性。设置该属性后,UIWindow会自动将view controller的view添加到当前window中,同时负责维护view controller和view的生命周期。上述在application:didFinishLaunchingWithOptions:中使用的就是这种办法;

系统对UIWindow的使用

通常在一个程序中只会有一个UIWindow,但有些时候我们调用系统的控件(例如UIAlertView)时,iOS系统为了保证UIAlertView在所有的界面之上,它会临时创建一个新的UIWindow,通过将其UIWindowLevel设置更高,让UIAlertView盖在所有其他UI之上。

为了验证这个说法,在《iOS开发进阶》中,作者还以举例形式图文并茂给出了说明。

WindowLevel

那不是不是新创建的UIWindow一定会覆盖在界面的最上面呢?其实并不是这样的。UIWindow有一个类型为“UIWindowLevel”的属性,该属性定义了UIWindow的层级,系统定义的WindowLevel一共有三种取值,如下所示:

1
2
3
UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar;

把这几个值打印出来,得到结果如下:

1
2
3
UIWindowLevelNormal=0.000000
UIWindowLevelAlert=2000.000000
UIWindowLevelStatusBar=1000.000000

从中能够看出,默认程序的UIWindow的层级是UIWindowLevelNormal,当系统需要覆盖在其上覆盖UIAlertView时,就会创建一个层级是UIWindowLevelAlert的UIWindow,因为其WindowLevel值更高,所以就覆盖在上面了。

手工创建UIWindow

有些时候,我们也希望在应用开发中,将某些界面覆盖在所有界面的最上层。这个时候,我们就可以手工创建一个新的UIWindow。需要注意的是,和创建UIView不同,UIWindow一旦被创建,它就自动地被添加到整个界面上了。

还有一点需要注意的是,如果我们创建的UIWindow需要处理键盘事件,那就需要合理地将其设置为keyWindow。keyWindow是被系统设计用来接收键盘和其他非触摸事件的UIWindow。我们可以通过makeKeyWindow和resignKeyWindow方法设置UIWindow实例的keyWindow与否。

P.S:在实际开发中发现,为了让UIWindow实例可见,一般需要调用makeKeyAndVisible方法,否则UIWindow实例没能正常呈现出来,简而言之,管理UIWindow的visible的方法除了makeKeyAndVisible之外没有找到类似于makeVisible的方法;因此不禁对和创建UIView不同,UIWindow一旦被创建,它就自动地被添加到整个界面上了这句话产生了怀疑…其实不用怀疑,控制UIWindow的visible与否的相关属性和其他UIView的属性一样,是hidden。所以,在不调用makeKeyAndVisible的情况下,UIWindow实例没能正常显示的原因是因为Window的hidden默认值为true,所以设置其为false就好了。

那么在哪些场合会涉及到“手工创建UIWindow”呢?参考唐巧在《iOS开发进阶》里的描述,我认为支付宝钱包等App的密码保护页面是基于UIWindow实现的,当用户从应用的任何界面按Home键退出,过一段时间再从后台切换回来时,显示一个密码输入界面。只有用户输入了正确的密码,才能进入退出前的界面。因为这个密码输入界面可能从任何应用界面弹出,并且需要盖住所有界面的最上层,所以很合适做一个UIWindow来实现。

P.S:我想至少有另外一个替换方案,这个方案不需要创建一个Window,具体的策略是:1. 找到当前Window;2. 找到当前ViewController;3. 在当前ViewController中以modal形式呈现一个新View Controller;更详细的介绍这里有描述。

除了类似于支付宝钱包App的手势解锁功能界面之外,其他适合用UIWindow来实现的功能还包括:应用的启动介绍页,应用内的通知提醒消息,应用内的弹出框广告等。

仿微信ActionSheet

笔者总觉得iOS原生的ActionSheet比较丑,如下:

iOS原生的ActionSheet除了能对button titles进行定义之外,不能进行更多其他的设置。

相较而言,微信自定义的ActionSheet漂亮得多,恰好通过唐巧大神的《iOS开发进阶》学习到了UIWindow的相关内容,所以决定使用UIWindow实现一个类似于微信的ActionSheet,最终效果如下:

Demo代码详见这里

UIWindow学习的更多相关文章

  1. IOS开发之XCode学习007:UIWindow对象

    此文学习来源为:http://study.163.com/course/introduction/1002858003.htm #import "AppDelegate.h" @i ...

  2. 【学习总结】IOS程序运行过程 、UIWindow 、controller 、 UIView 创建过程的总结

    程序启动开始到view显示: 程序启动首先会执行main函数 - > UIApplicationMain函数: 程序启动 (加载框架,info文件,资源等) 执行Main函数 初始化UIAppl ...

  3. UIView 和 UIWindow 的学习内容

    UIWindow是UIView的子类,一个程序只能有一个window主窗口. 在XCode7之后我们创建UIWindow的对象,代码如下: //创建一个窗口,使其铺满屏幕(设置大小)         ...

  4. IOS学习笔记(五)——UI基础UIWindow、UIView

    在PC中,应用程序多是使用视窗的形式显示内容,手机应用也不例外,手机应用中要在屏幕上显示内容首先要创建一个窗口承载内容,iOS应用中使用UIWindow.UIView来实现内容显示. UIWindow ...

  5. iOS UIWindow 与 windowLevel 学习

    Pop几个关键点 KeyWindow :”The key window is the one that is designated to receive keyboard and other non- ...

  6. 【原】iOS学习之事件处理的原理

    在iOS学习23之事件处理中,小编详细的介绍了事件处理,在这里小编叙述一下它的相关原理 1.UITouch对象 在触摸事件的处理方法中都会有一个存放着UITouch对象的集合,这个参数有什么用呢? ( ...

  7. iOS阶段学习第33天笔记(自定义标签栏(UITabBar)介绍)

    iOS学习(UI)知识点整理 一.自定义标签栏 1.方法一 单个创建标签栏 #import "AppDelegate.h" #import "SecondViewCont ...

  8. Coding源码学习第二部分(FunctionIntroManager.m)

    接上篇.上篇有一个细节忘了写,在Coding_iOS-Info.plist 里面添加了一个key 是 Status bar is initially hidden  Value 是 YES,在appl ...

  9. Coding源码学习第一部分(AppDelegate.m)

    前言:在此首先感谢开源,感谢大神们的无私分享. Coding 的主页:https://coding.net/app#app-feature Coding 自己家的仓库:https://coding.n ...

随机推荐

  1. hdu 1754 splay tree伸展树 初战(单点更新,区间属性查询)

    题意:与区间查询点更新,点有20W个,询问区间的最大值.曾经用线段树,1000+ms,今天的伸展树,890没ms,差不多. 第一次学习伸展树,一共花了2个单位时间,感觉伸展树真很有用,也很好玩.现在只 ...

  2. 无法启动INTERNET连接共享。一个已经用IP地址配置的LAN连接需要自动IP地址

    提问者采纳   很简单 只要把你要共享的连接 192.168.0.1 0改成1 然后就可以共享了 共享后再改回来 就能上了 但是会出现IP冲突的~

  3. flask如何使模板返回大文件,又不消耗大量内存

    当我们要往客户端发送大量的数据,比如一个大文件时,将它保存在内存中再一次性发到客户端开销很大.比较好的方式是使用流,本篇就要介绍怎么在Flask中通过流的方式来将响应内容发送给客户端.此外,我们还会演 ...

  4. 创建注记图层C# IFeatureWorkspaceAnno

    http://blog.csdn.net/mydriverc/article/details/1675613     //IFeatureWorkspaceAnno Example     //The ...

  5. iOS开发之创建颜色渐变视图View

    在iOS开发中有时需要自己自定义一个视图view的背景,而网上有人提出的在循环中不断alloc的方法设置其背景色渐变,会耗费很多内存和资源,极其不明智,而在CALayer中早就提供有图层渐变的类和相应 ...

  6. 在java中String类为什么要设计成final?

    大神链接:在java中String类为什么要设计成final? - 程序员 - 知乎 我进行了重新排版,并且更换了其中的一个例子,让我们更好理解. String很多实用的特性,比如说“不可变性”,是工 ...

  7. 线程池实例:使用Executors和ThreadPoolExecutor

    线程池负责管理工作线程,包含一个等待执行的任务队列.线程池的任务队列是一个Runnable集合,工作线程负责从任务队列中取出并执行Runnable对象. java.util.concurrent.ex ...

  8. Linux变量内容的删除、代替与替换

    变量内容的删除与代替 范例一:先让小写的 path 自己定义变量配置的与 PATH 内容同样 [root@www ~]# path=${PATH} [root@www ~]# echo $path / ...

  9. ubuntu编译airplay

    1.alsa/asoundlib.h: No such file or directory 缺少一个库: apt-get install libasound2-dev   2.fatal error: ...

  10. java的nio包的SelectionKey,Selector,SelectableChannel三者的缠绵关系概述

    猛击这里 java的nio包的SelectionKey,Selector,SelectableChannel三者的缠绵关系概述