UIWindow

Window的作用


在iOS中,一个window(也就是一个UIWindow对象)主要有这样几个作用:

  1. 展示app的可视内容;
  2. 将事件分发给视图以及其他对象;
  3. 和app的view controller一起处理屏幕旋转。

其实在大多数情况下,第三方程序员不用做任何事情,window就能完成这些工作。所以很多时候,只有当这个app需要支持另一个外设的屏幕的时候,程序员才会对window进行操作。


创建window


有好几种创建window的方式:

使用storyboard:

如果程序员为app创建了一个storyboard,并在info.plist中指定它为main storyboard,那么在app启动的时候,iOS会自动帮程序员做这样几件事情:

  1. 实例化一个window;
  2. 加载main storyboard,并且实例化其中的root view controller;
  3. 将这个view controller赋值给window.rootViewController,并显示这个window。
使用nib文件:

如果不使用storyboard,也可以用nib文件来代替。将一个window对象拖拽到Interface Builder文件中,并将这个文件指定为app的main interface。那么在app启动的时候,iOS也会自动创建window对象。
为了确保window的大小与屏幕大小吻合,需要在Interface Builder中对window对象勾选Full Screen at Launch这个属性。

手写代码:

当然也可以通过手写代码的方式创建window。比如官方示例代码:

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

    UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    myViewController = [[MyViewController alloc] init];
    window.rootViewController = myViewController;
    [window makeKeyAndVisible];

    return YES;
}

需要注意的是,window的尺寸永远应该是屏幕的尺寸,不应该考虑状态栏等元素,因为这些是view controller应该处理的问题。


Action Sheet和Alert View


知道了window的存在之后,感觉也能知道很多事情。

比如,iOS中的UIActionSheet和UIAlertView其实是显示在另一个window上的。

监听UIWindowDidResignKeyNotification,可以发现,当action sheet弹出时,UIWindowDidResignKeyNotification通知被发送了。此时检查app所在的window,发现它已经不再是key window了。


悬浮窗


以前我总以为所有的view都是被拘束在UIViewController的view中的,所以一直不知道悬浮窗的效果应该如何实现。但实际上,UIWindow本身就是一个UIView,可以直接在UIWindow上添加子视图,做出悬浮的效果。(虽然这样不符合苹果的设计规范)

[[[UIApplication sharedApplication].delegate window] addSubview:suspendView];

呈现出视图悬浮在app之上的效果:


悬浮窗效果

黑科技?


好奇尝试了一些奇怪的情景O.O

如果window没有占满整个屏幕会怎样呢?

默认情况下,window中的视图依然能照常显示,但是触屏事件无法正常分发。

官方文档中这样描述:

Because a window doesn’t receive touch events outside of its bounds and views aren’t clipped to the window’s bounds by default, an improperly sized window might not be able to deliver touch events to all its views.

用手写代码的方式可以创建一个任意大小的window,比如在application:willFinishLaunchingWithOptions:方法中写:

self.window = [[XSQWindow alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];

运行程序时,window中的子视图依然可以照常显示,包括window外的部分,但是在点击window之外的区域时,终端会输出错误信息:

unexpected nil window in _UIApplicationHandleEventFromQueueEvent, _windowServerHitTestWindow: <XSQWindow: 0x14c614a20; baseClass = UIWindow; frame = (0 0; 200 200); gestureRecognizers = <NSArray: 0x174059740>; layer = <UIWindowLayer: 0x174220e60>>

并且触屏点对应的视图无法接收到这次的触屏事件。

如果在一个app中创建多个window会怎么样?

也是可以做到在一个app中创建多个window的,而且似乎也不会怎么样。

















一、UIWindow是一种特殊的UIView,通常在一个程序中只会有一个UIWindow,但可以手动创建多个UIWindow,同时加到程序里面。UIWindow在程序中主要起到三个作用:

  1、作为容器,包含app所要显示的所有视图

  2、传递触摸消息到程序中view和其他对象

  3、与UIViewController协同工作,方便完成设备方向旋转的支持

二、通常我们可以采取两种方法将view添加到UIWindow中:

  1、addSubview

  直接将view通过addSubview方式添加到window中,程序负责维护view的生命周期以及刷新,但是并不会为去理会view对应的ViewController,因此采用这种方法将view添加到window以后,我们还要保持view对应的ViewController的有效性,不能过早释放。

  2、rootViewController

  rootViewController时UIWindow的一个遍历方法,通过设置该属性为要添加view对应的ViewController,UIWindow将会自动将其view添加到当前window中,同时负责ViewController和view的生命周期的维护,防止其过早释放

三、WindowLevel

  UIWindow在显示的时候会根据UIWindowLevel进行排序的,即Level高的将排在所有Level比他低的层级的前面。下面我们来看UIWindowLevel的定义:

    const UIWindowLevel UIWindowLevelNormal;
    const UIWindowLevel UIWindowLevelAlert;
    const UIWindowLevel UIWindowLevelStatusBar;
    typedef CGFloat UIWindowLevel;

  IOS系统中定义了三个window层级,其中每一个层级又可以分好多子层级(从UIWindow的头文件中可以看到成员变量CGFloat _windowSublevel;),不过系统并没有把则个属性开出来。UIWindow的默认级别是UIWindowLevelNormal,我们打印输出这三个level的值分别如下:

[objc]  view plain  copy
  1. 2012-03-27 22:46:08.752 UIViewSample[395:f803] Normal window level0.000000  
  2. 2012-03-27 22:46:08.754 UIViewSample[395:f803] Normal window level2000.000000  
  3. 2012-03-27 22:46:08.755 UIViewSample[395:f803] Normal window level1000.000000  

  这样印证了他们级别的高低顺序从小到大为Normal < StatusBar < Alert,下面请看小的测试代码:

TestWindowLevel
复制代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
self.window.backgroundColor = [UIColor yellowColor];
[self.window makeKeyAndVisible];

UIWindow *normalWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
normalWindow.backgroundColor = [UIColor blueColor];
normalWindow.windowLevel = UIWindowLevelNormal;
[normalWindow makeKeyAndVisible];

CGRect windowRect = CGRectMake(50,
50,
[[UIScreen mainScreen] bounds].size.width - 100,
[[UIScreen mainScreen] bounds].size.height - 100);
UIWindow *alertLevelWindow = [[UIWindow alloc] initWithFrame:windowRect];
alertLevelWindow.windowLevel = UIWindowLevelAlert;
alertLevelWindow.backgroundColor = [UIColor redColor];
[alertLevelWindow makeKeyAndVisible];

UIWindow *statusLevelWindow = [[UIWindow alloc] initWithFrame:CGRectMake(0, 50, 320, 20)];
statusLevelWindow.windowLevel = UIWindowLevelStatusBar;
statusLevelWindow.backgroundColor = [UIColor blackColor];
[statusLevelWindow makeKeyAndVisible];

NSLog(@"Normal window level: %f", UIWindowLevelNormal);
NSLog(@"Normal window level: %f", UIWindowLevelAlert);
NSLog(@"Normal window level: %f", UIWindowLevelStatusBar);

return YES;
}
复制代码

  运行结果如下图:

  我们可以注意到两点:

  1)我们生成的normalWindow虽然是在第一个默认的window之后调用makeKeyAndVisible,但是仍然没有显示出来。这说明当Level层级相同的时候,只有第一个设置为KeyWindow的显示出来,后面同级的再设置KeyWindow也不会显示。

  2)statusLevelWindow在alertLevelWindow之后调用makeKeyAndVisible,淡仍然只是显示在alertLevelWindow的下方。这说明UIWindow在显示的时候是不管KeyWindow是谁,都是Level优先的,即Level最高的始终显示在最前面。










三、如何获取window?

1.主窗口和次窗口

【self.window makekeyandvisible】让窗口成为主窗口,并且显示出来。有这个方法,才能把信息显示到屏幕上。

   因为Window有makekeyandvisible这个方法,可以让这个Window凭空的显示出来,而其他的view没有这个方法,所以它只能依赖于Window,Window显示出来后,view才依附在Window上显示出来。

【self.window make keywindow】//让uiwindow成为主窗口,但不显示。

 

2.获取UIwindow

(1)[UIApplication sharedApplication].windows  在本应用中打开的UIWindow列表,这样就可以接触应用中的任何一个UIView对象(平时输入文字弹出的键盘,就处在一个新的UIWindow中)

(2)[UIApplication sharedApplication].keyWindow(获取应用程序的主窗口)用来接收键盘以及非触摸类的消息事件的UIWindow,而且程序中每个时刻只能有一个UIWindow是keyWindow。

提示:如果某个UIWindow内部的文本框不能输入文字,可能是因为这个UIWindow不是keyWindow

(3)view.window获得某个UIView所在的UIWindow





什么是keyWindow,官方文档中是这样解释的" The key window is the one that is designated to receive keyboard and other non-touch related events. Only one window at a time may be the key window. " 翻译过来就是说,keyWindow是指定的用来接收键盘以及非触摸类的消息,而且程序中每一个时刻只能有一个window是keyWindow。

http://www.cnblogs.com/smileEvday/archive/2012/11/16/UIWindow.html








关于系统对UIWindow的使用。
通常在一个程序中只会有一个UIWindow,但有些时候我们调用系统的控件(例如UIAlertView)时,IOS系统为了保证UIAlertView在所有的界面之上,它会临时创建一个新的UIWindow,通过将其UIWindow的UIWindowLevel设置的更高,让UIWindow盖在所有的应用界面之上(熟悉html的朋友应该知道,网上上面的“遮罩效果”,就是通过设置元素的z-index属性,来控制层级的上下关系,应该是一个道理)。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值