写在前面

本文内容绝大部分都参考唐巧大神的《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. springmvc中ajax处理

    1.使用HttpServletResponse处理--不需要配置解析器 @Controller public class AjaxController { @RequestMapping(" ...

  2. web信息搜索之目标扫描篇

    https://blog.csdn.net/dongfei2033/article/details/78175421

  3. HDU 6149 Valley Numer II(状压DP)

    题目链接 HDU6149 百度之星复赛的题目……比赛的时候并没有做出来. 由于低点只有15个,所以我们可以考虑状压DP. 利用01背包的思想,依次考虑每个低点,然后枚举每个状态. 在每个状态里面任意枚 ...

  4. idea修改变量及其引用

    idea 修改某一变量及其引用 选中变量 shift+f6(shift+fn+f6), ctrl+R的当前页面全局替换, ctrl+shift+R 项目中的全局替换

  5. 51 NOD 1406 and query

    我们知道一个数S会对所有它的子集S'产生1的贡献,但是我们直接枚举子集是 3^(log2 1000000)的,会炸掉:如果直接把每个有1的位变成0往下推也会凉掉,因为这样会有很多重复的. 但是我们发现 ...

  6. codeforces #463

    D(树上倍增) 题意: 刚开始有一个点1,权值为0. 接下来有q个操作,每个操作有两种: 1 R W:新加一个点,这个点的权值为W,这个点的父亲是R 2 R X:在从点R到1的路径上,取出从R开始的不 ...

  7. Java中常量定义在interface和class的区别(转)

    最终结论:定义常量在interface和class中其实都行,关键是看你的设计和个人爱好. Java中interface中定义变量默认都是"public static final" ...

  8. Mac安装IntelliJ IDEA时快捷键冲突设置

    Mac有专门的快捷键,和Linux/Windows的不一样. 下面是发现的一些需要屏蔽的快捷键: 一.搜狗输入法: 暂时没发现有冲突. 二.系统 代码提示:Ctrl+空格(输入法开关) 三.其它 暂无 ...

  9. Nginx常用命令(启动/重启/停止/测试配置文件/重新加载配置文件)

    Nginx 安装后只有一个程序文件,本身并不提供各种管理程序,它是使用参数和系统信号机制对 Nginx 进程本身进行控制的. Nginx 的参数包括有如下几个: 使用: /usr/local/ngin ...

  10. 第24章、OnLongClickListener长按事件(从零开始学Android)

    在Android App应用中,OnLongClick事件表示长按2秒以上触发的事件,本章我们通过长按图像设置为墙纸来理解其具体用法. 知识点:OnLongClickListener OnLongCl ...