好久没有更新博客了O.O因为最近工作好忙TAT
当然忙的时候也积累了很多以前没注意的问题。所以又有了很多能写成博客的素材O.O
所以这次先来研究一下我以前从来没注意过的UIWindow好了。
Window的作用
在iOS中,一个window(也就是一个UIWindow对象)主要有这样几个作用:
- 展示app的可视内容;
- 将事件分发给视图以及其他对象;
- 和app的view controller一起处理屏幕旋转。
其实在大多数情况下,第三方程序员不用做任何事情,window就能完成这些工作。所以很多时候,只有当这个app需要支持另一个外设的屏幕的时候,程序员才会对window进行操作。
创建window
有好几种创建window的方式:
使用storyboard:
如果程序员为app创建了一个storyboard,并在info.plist中指定它为main storyboard,那么在app启动的时候,iOS会自动帮程序员做这样几件事情:
- 实例化一个window;
- 加载main storyboard,并且实例化其中的root view controller;
- 将这个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的,而且似乎也不会怎么样。
触屏事件会根据触摸点的位置,被UIApplication分发到对应的window中。
参考
UIWindow Class Reference
Understanding Windows and Screens
iOS开发UI篇—控制器的创建
关于UIWindow的一点儿思考
Event Delivery: The Responder Chain