深入了解iOS开发中UIWindow的相关使用
uiwindow是一种特殊的uiview,通常在一个app中只会有一个uiwindow。
ios程序启动完毕后,创建的第一个视图控件就是uiwindow,接着创建控制器的view,最后将控制器的view添加到uiwindow上,于是控制器的view就显示在屏幕上了。
一个ios程序之所以能显示到屏幕上,完全是因为它有uiwindow。也就说,没有uiwindow,就看不见任何ui界面。
如何获取uiwindow
(1)[uiapplication sharedapplication].windows 在本应用中打开的uiwindow列表,这样就可以接触应用中的任何一个uiview对象(平时输入文字弹出的键盘,就处在一个新的uiwindow中);
(2)[uiapplication sharedapplication].keywindow(获取应用程序的主窗口)用来接收键盘以及非触摸类的消息事件的uiwindow,而且程序中每个时刻只能有一个uiwindow是keywindow;
注:经过代码验证,非keywindow 也是可以接受键盘消息的;
提示:如果某个uiwindow内部的文本框不能输入文字,可能是因为这个uiwindow不是keywindow;
(3)view.window获得某个uiview所在的uiwindow。
uiwindowlevel
我们知道uiwindow 有三个层级,分别是normal ,statusbar,alert.输出他们三个层级的值,我们发现从左到右依次是0,1000,2000,也就是说normal级别是最低的,statusbar处于中级,alert级别最高。而通常我们的程序的界面都是处于normal这个级别的,系统顶部的状态栏应该是处于statusbar级别,uiactionsheet和uialertview这些通常都是用来中断正常流程,提醒用户等操作,因此位于alert级别。
根据window显示级别优先原则,级别高的会显示在最上层,级别低的在下面,我们程序正常显示的view在最底层;
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。
观察uiwindow的文档,我们可以发现里面有四个关于window变化的通知:
uiwindowdidbecomevisiblenotification
uiwindowdidbecomehiddennotification
uiwindowdidbecomekeynotification
uiwindowdidresignkeynotification
这四个通知对象中的object都代表当前已显示(隐藏),已变成keywindow(非keywindow)的window对象,其中的userinfo则是空的。于是我们可以注册这个四个消息,再打印信息来观察keywindow的变化以及window的显示,隐藏的变动
变成keywindow 的流程是这样的
1.程序默认的window先显示出来
2.默认的window再变成keywindow
3.alertview 的window显示出来
4.默认的window变成keywindow
5.最终alertview的window变成keywindow
ios8开始uiwindow的bounds发生变化(window本身发生了旋转)
ios 7之前window的bounds不会随着方向而变化,但是到了ios 8以后,随着设备方向的旋转,window.bounds.size.width和window.bounds.size.height也会相应发生变化。
做个很简单的测试,代码如下:
- (bool)application:(uiapplication *)application didfinishlaunchingwithoptions:(nsdictionary *)launchoptions {
// override point for customization after application launch.
[[nsnotificationcenter defaultcenter] addobserver:self
selector:@selector(orientationchanged:)
name:uideviceorientationdidchangenotification
object:nil];
return yes;
}
- (void)orientationchanged:(nsnotification*)noti {
uideviceorientation orientation = [uidevice currentdevice].orientation;
nsstring *orientationdes = nil;
switch (orientation) {
case uideviceorientationlandscapeleft:
orientationdes = @"uiinterfaceorientationlandscaperight";
break;
case uideviceorientationlandscaperight:
orientationdes = @"uiinterfaceorientationlandscapeleft";
break;
case uideviceorientationportrait:
orientationdes = @"uiinterfaceorientationportrait";
break;
case uideviceorientationportraitupsidedown:
orientationdes = @"uiinterfaceorientationportraitupsidedown";
break;
default:
orientationdes = @"";
break;
}
nslog(@"system ver: %@, \rorientaion: %@, \rwindow bounds: %@",
[uidevice currentdevice].systemversion,
orientationdes,
nsstringfromcgrect(self.window.bounds));
}
示例代码很简单,新建一个工程,然后在delegate中直接添加以上代码即可。
ios 8上运行结果为:
2014-06-04 09:26:32.016 svios8[4143:61114] system ver: 8.0,
orientaion: uiinterfaceorientationlandscaperight,
window bounds: {{0, 0}, {320, 480}}
2014-06-04 09:26:34.788 svios8[4143:61114] system ver: 8.0,
orientaion: uiinterfaceorientationportrait,
window bounds: {{0, 0}, {480, 320}}
2014-06-04 09:26:35.791 svios8[4143:61114] system ver: 8.0,
orientaion: uiinterfaceorientationlandscapeleft,
window bounds: {{0, 0}, {320, 480}}
2014-06-04 09:26:47.468 svios8[4143:61114] system ver: 8.0,
orientaion: uiinterfaceorientationportraitupsidedown,
window bounds: {{0, 0}, {480, 320}}
ios 7及之前的版本运行结果为:
orientaion: uiinterfaceorientationlandscaperight,
window bounds: {{0, 0}, {320, 480}}
2014-06-04 09:39:00.895 svios8[4380:70b] system ver: 7.0.3,
orientaion: uiinterfaceorientationportrait,
window bounds: {{0, 0}, {320, 480}}
2014-06-04 09:39:01.225 svios8[4380:70b] system ver: 7.0.3,
orientaion: uiinterfaceorientationlandscapeleft,
window bounds: {{0, 0}, {320, 480}}
2014-06-04 09:39:11.004 svios8[4380:70b] system ver: 7.0.3,
orientaion: uiinterfaceorientationportraitupsidedown,
window bounds: {{0, 0}, {320, 480}}
通过对比我们可以清晰的看到ios 8中uiwindow在处理旋转时策略的变更,虽然会因为与之前的版本不同导致现有项目布局存在的bug,但是可以看到ios 8中的处理方式更加符合我们的预期,在竖向的时候我们就获取到width < height, 在横向则是 width > height,这个符合所见即所得的原则。
题外话,不管是ios 7还是之前的版本,以及最新出的ios 8,所有的viewcontroller的bounds都是正确的,所以只需要坚持一个原则“所有布局都是基于vc.view.bounds布局,那么你的app的显示就一切正常。”
上一篇: AI怎么将图片制作成手绘素描效果?
下一篇: 让男友拍出好看照片的几个技巧