12.关于UIWindow的总结

一、UIWindow是一种特殊的UIView,通常在一个程序中只会有一个UIWindow,但可以手动创建多个UIWindow,同时加到程序里面。UIWindow在程序中主要起到三个作用:
1、作为容器,包含app所要显示的所有视图
2、传递触摸消息到程序中view和其他对象
3、与UIViewController协同工作,方便完成设备方向旋转的支持

二、UIWindow 在日常开发中遇到的一些问题的总结:
1.四大对象的关系图(UIApplication/AppDelegate/UIWindow/UIViewController)
12.关于UIWindow的总结_第1张图片
2.AppDelegate 中创建 UIWindow

#import "AppDelegate.h"
#import "RootViewController.h"
@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.

    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    //创建 RootViewController 对象,并设置为 window 的根视图控制器
    self.window.rootViewController = [[RootViewController alloc] init];

    return YES;
}

3.UIWindow 在普通代码和 IB 中的创建过程
(1)在有storyboard中的创建过程: 先执行Main函数,执行UIApplicationMain(),根据其第三个和第四个参数创建Application,创建代理,并且把代理设置给application,根据项目配置文件info.plist里面的storyboard的name,找到对应的storyboard,接下来创建一个window,之后创建它的初始化控制器(就是箭头所指向的控制器),自动把该控制器设置为UIWindow的根控制器,接下来再将window显示出来,即看到了运行后显示的界面。
(2)在没有storyboard中的创建过程: 先执行Main函数,执行UIApplicationMain(),根据其第三个和第四个参数创建Application,创建代理,并且把代理设置给application,开启一个事件循环,当程序加载完毕,他会调用代理的didFinishLaunchingWithOptions:方法。在该方法中,会创建一个Window,然后创建一个控制器,并把该控制器设置为UIWindow的根控制器,接下来再将window显示出来,即看到了运行后显示的界面。
4.主/次窗口
makeKeyAndVisible方法:让窗口成为主窗口,并且显示出来。有这个方法,才能把信息显示到屏幕上。因为Window有makekeyandvisible这个方法,可以让这个Window凭空的显示出来,而其他的view没有这个方法,所以它只能依赖于Window,Window显示出来后,view才依附在Window上显示出来。
makekeywindow方法:让uiwindow成为主窗口,但不显示。
5.窗口销毁
UIWindow 有一个方法是resignKeyWindow, 官方文档如下:

Called automatically to inform the window that it is no longer the key window.
Never call this method directly. The system calls this method and posts UIWindowDidResignKeyNotification to let the window know when it is no longer key. The default implementation of this method does nothing, but subclasses can override it and use it to perform tasks related to resigning the key window status.

这个方法当window不在是主窗口时自动调用, 不可以直接调用, 但可以在自定义子类中重写此方法.
但是, 在开发中, 我发现有些时候是可以直接调用此方法的, 而且可以解决一些关于 window 之间切换的问题. 所以, 我在用的时候, 只是保证尽量不去直接调用, 实在需要的情况下才会直接调用的.

6.获取UIwindow
(1)[UIApplication sharedApplication].windows 在本应用中打开的UIWindow列表,这样就可以接触应用中的任何一个UIView对象(平时输入文字弹出的键盘,就处在一个新的UIWindow中)
(2)[UIApplication sharedApplication].keyWindow(获取应用程序的主窗口)用来接收键盘以及非触摸类的消息事件的UIWindow,而且程序中每个时刻只能有一个UIWindow是keyWindow。
提示:如果某个UIWindow内部的文本框不能输入文字,可能是因为这个UIWindow不是keyWindow
(3)在开发中,某些时候用(2)中的方法获取当前主窗口不起作用, 但是通过[[UIApplication sharedApplication]delegate] window]方法获得的可以起作用。
(4)view.window获得某个UIView所在的UIWindow
7.UIWindowLevel
UIWindow有三个层级,分别是Normal,StatusBar,Alert。打印输出他们三个这三个层级的值我们发现从左到右依次是0,1000,2000,也就是说Normal级别是最低的,StatusBar处于中等水平,Alert级别最高。而通常我们的程序的界面都是处于Normal这个级别上的,系统顶部的状态栏应该是处于StatusBar级别,UIActionSheet和UIAlertView这些通常都是用来中断正常流程,提醒用户等操作,因此位于Alert级别。

typedef CGFloat UIWindowLevel;
@property(nonatomic) UIWindowLevel windowLevel;                   // default = 0.0

UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar __TVOS_PROHIBITED;
//

既然三个级别的值之间相差1000,但是在 Demo 中打印windowLevel 的值会发现系统的UIAlertView的级别是1996,而与此同时UIActionSheet的级别是2001。这样说明了实际上系统中的某些类, 不只有上面三个级别, 而是在这三个级别下进行微调来区分开的。
根据window显示级别优先的原则,级别高的会显示在上面,级别低的在下面,我们程序正常显示的view位于最底层,至于具体怎样获取UIAlertView和UIActionSheet的level,我会在下面第二部分keyWindow中介绍并给出相应的代码。
12.关于UIWindow的总结_第2张图片
8.关于 keyWindow
什么是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。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    // Override point for customization after application launch.
    self.viewController = [[[SvUIWindowViewController alloc] initWithNibName:@"SvUIWindowViewController" bundle:nil] autorelease];
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];

    UIWindow *window1 = [[UIWindow alloc] initWithFrame:CGRectMake(0, 80, 320, 320)];
    window1.backgroundColor = [UIColor redColor];
    window1.windowLevel = UIWindowLevelAlert;
    [window1 makeKeyAndVisible];

    return YES;
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    [self registerObserver];

    // add a textfield
    UITextField *filed = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 320, 60)];
    filed.placeholder = @"Input something here";
    filed.clearsOnBeginEditing = YES;
    filed.borderStyle = UITextBorderStyleRoundedRect;
    [self.view addSubview:filed];
}

运行截图如下:

从图中可以看出,虽然我们自己新建了一个然后设置为keyWindow并显示,但是点击程序中默认window上添加的textField还是可以唤出键盘,而且还可以正常接受键盘输入,只是键盘被挡住了,说明非keyWindow也是可以接受键盘消息。

当弹出 AlertView 或者 SheetView 的时候(它们自身弹出时带有一个 UIWindow), 所以两个 UIWindow的状态是这样的:
1、程序默认的window先显示出来
2、默认的window再变成keyWindow
3、AlertView的window显示出来
4、默认的window变成非keyWindow
5、最终AlertView的window变成keyWindow

当收回 AlertView 或者 SheetView 的时候,两个 UIWindow的状态是这样的:
1、首先AlertView的window变成非keyWindow
2、程序默认的window变成keyWindow
3、AlertView的window再隐藏掉

参考资料:UIWindow的一点儿思考

你可能感兴趣的:(keywindow,UIWindow,window层次,window创建过程,window-总结)