UIWindow知识归纳
UIView的功能
负责渲染区域的内容,并且响应该区域内发生的触摸事件
UIWindow
在iOS App中,UIWindow是最顶层的界面内容,我们使用UIWindow和UIView来呈现界面。UIWindow并不包含任何默认的内容,但是它被当作UIView的容器,用于放置应用中所有的UIView。
从继承关系来看,UIWindow继承自UIView,所以UIWindow除了具有UIView的所有功能之外,还增加了一些特有的属性和方法,而我们最常用的方法,就是在App刚启动时,调用UIWindow的rootViewController(必须指定根控制器) 和 makeKeyAndVisible方法
状态栏和键盘都是特殊的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的生命周期的维护,防止其过早释放
两个方法的区别:
以后的开发中,建议使用(2).因为方法(1)存在一些问题,比如说控制器上面可能由按钮,需要监听按钮的点击事件,如果是1,那么按钮的事件应该由控制器来进行管理。但控制器是一个局部变量,控制器此时已经不存在了,但是控制器的view还在,此时有可能会报错。注意:方法执行完,这个控制器就已经不存在了。
问题描述1:当view发生一些事件的时候,通知控制器,但是控制器已经销毁了,所以可能出现未知的错误。
问题描述2:添加一个开关按钮,让屏幕360度旋转(两者的效果不一样)。当发生屏幕旋转事件的时候,UIapplication对象会将旋转事件传递给uiwindow,uiwindow又会将旋转事件传递给它的根控制器,由根控制器决定是否需要旋转
UIapplication->uiwindow->根控制器(第一种方式没有根控制器,所以不能跟着旋转)。
提示:不通过控制器的view也可以做开发,但是在实际开发中,不要这么做,不要直接把view添加到UIWindow上面去。因为,难以管理。
WindowLevel
UIWindow的层级由一个UIWindowLevel类型属性windowLevel,该属性指示了UIWindow的层级,windowLevel有三种可取值。
并且层级是可以做加减的self.window.windowLevel = UIWindowLevelAlert+1;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal; //默认,值为0
UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert; //值为2000
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar ; // 值为1000
Normal ,StatusBar,Alert.输出他们三个层级的值,我们发现从左到右依次是0,1000,2000,也就是说Normal级别是最低的,StatusBar处于中级,Alert级别最高。而通常我们的程序的界面都是处于Normal这个级别的,系统顶部的状态栏应该是处于StatusBar级别,提醒用户等操作位于Alert级别。根据window显示级别优先原则,级别高的会显示在最上层,级别低的在下面,我们程序正常显示的view在最底层;
如何获取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
当前app可以打开的多个window 如系统状态栏其实就是一个window ,程序启动的时候创建的默认的window ,弹出键盘也是一个window ,alterView 弹框也是window 。但是keyWindow只有一个 ,一般情况下就是我们程序启动时设置的默认的window
官方文档中是这样解释的 “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。
问题一:一个应用程序只能有一个主窗口,如果程序中创建了两个Window,那么谁是主窗口?
①iOS 7 以后,主窗口和次窗口是没有区别的
②iOS 7 之前,如果后面的窗口设置为主窗口,会把之前设置的主窗口覆盖掉
问题二:只有主窗口才能响应键盘的输入事件?
在ios9.3的模拟器中,主窗口和非主窗口中的输入框都能输入文字,但是在ios6.1的模拟器中,非主窗口的输入框不能输入文字。 获取keyWindow的方式
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
UIViewController *rootViewController = keyWindow.rootViewController;
注意:keyWindow不是一成不变的,当你创建alertView或者ActionSheet的时候,它们所在的window会变成keyWindow。也就是说系统默认创建的window首先变成keywindow,而当弹框的时候,alertView所在的window变成keywindow,默认的keywindow变成非keywindow。
@property(nonatomic,readonly) NSArray *windows;在windows数组里面,window是根据windowLevel来排列的,最后一个覆盖在最上面。这里的windows数组不包括系统提供的window,比如说状态栏就是在一个系统创建的window里面。
测试代码如下:
#import "AppDelegate.h"
@interface AppDelegate (
)@property(strong, nonatomic) UIWindow *normalWindow;
@property(strong, nonatomic) UIWindow *coverStatusBarWindow;
@property(strong, nonatomic) UIWindow *alertLevelWindow;
@end
@implementation AppDelegate
- (void)coverWindowOnClicked{
NSLog(@"tap tap 11111");
[[NSNotificationCenter defaultCenter]postNotificationName:@"kOnClickedStatusBarNotification" object:self userInfo:nil];}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event{
NSLog(@"touchesBegan touchesBegan55555555555");
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//1.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor yellowColor];
self.window.rootViewController = [[UIViewController alloc]init];
[self.window makeKeyAndVisible];
NSLog(@"1hahah%f",[UIApplication sharedApplication].keyWindow.windowLevel);
//2.
UIWindow *normalWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
normalWindow.backgroundColor = [UIColor grayColor];
normalWindow.windowLevel = UIWindowLevelNormal;
normalWindow.rootViewController = [[UIViewController alloc]init];
[normalWindow makeKeyAndVisible];
self.normalWindow = normalWindow;
UITextField *tf = [[UITextField alloc] init];
tf.frame = CGRectMake(10, 64, 100, 20);
tf.borderStyle = UITextBorderStyleRoundedRect;
[self.normalWindow addSubview:tf];
UITapGestureRecognizer *tap1 = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(coverWindowOnClicked)];
[self.normalWindow addGestureRecognizer:tap1];
NSLog(@"2hahah%f",[UIApplication sharedApplication].keyWindow.windowLevel);
//2. 创建覆盖着状态栏的window
UIWindow * coverStatusBarWindow =[[UIWindow alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 20)];
coverStatusBarWindow.rootViewController = [[UIViewController alloc]init];
coverStatusBarWindow.backgroundColor = [UIColor redColor];
//级别要比 状态栏的级别高
coverStatusBarWindow.windowLevel = UIWindowLevelStatusBar+1;
[coverStatusBarWindow makeKeyAndVisible];
self.coverStatusBarWindow = coverStatusBarWindow;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(coverWindowOnClicked)];
[self.coverStatusBarWindow addGestureRecognizer:tap];
//想移除coverStatusBarWindow 将其赋值为空
// self.coverStatusBarWindow = nil;
// 3.创建UIwindow1
self.alertLevelWindow = [[UIWindow alloc] initWithFrame:CGRectMake(50, 150, 200, 250)];
self.alertLevelWindow.backgroundColor = [UIColor blueColor];
UIViewController *vc1 = [[UIViewController alloc] init];
self.alertLevelWindow.rootViewController = vc1;
self.alertLevelWindow.windowLevel = UIWindowLevelAlert;
[self.alertLevelWindow makeKeyAndVisible];
// 给UIwindow1添加一个输入框
UITextField *tf1 = [[UITextField alloc] init];
tf1.frame = CGRectMake(10, 64, 100, 20);
tf1.borderStyle = UITextBorderStyleRoundedRect;
[self.alertLevelWindow addSubview:tf1];
UITapGestureRecognizer *tap2 = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(coverWindowOnClicked)];
[self.alertLevelWindow addGestureRecognizer:tap2];
NSLog(@"1hahah%f",[UIApplication sharedApplication].keyWindow.windowLevel);
NSLog(@"windows 刚启动 ---%@",[UIApplication sharedApplication].windows);
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyBoardShow:) name:UIKeyboardDidShowNotification object:nil];
return YES;
}
- (void)keyBoardShow:(NSNotification *)notif{
NSLog(@"windows 键盘弹出 ---%@",[UIApplication sharedApplication].windows);
NSLog(@"2hahah%f",[UIApplication sharedApplication].keyWindow.windowLevel);
}
可以得出以下结论: (实践是检验真理的唯一标准 网上有很多是错误的还是自己实践最好)
1) 同一层级的 最后一个显示出来,上一个被覆盖
2)UIWindow在显示的时候是不管KeyWindow是谁,都是Level优先的,即Level最高的始终显示在最前面。
3)谁最后设置的 makeKeyAndVisible 谁就是keyWindow 其他的也会显示出来 所有的window都可以监听键盘 和点击的事件