美文网首页
UIWindow知识归纳

UIWindow知识归纳

作者: 小蘑菇2 | 来源:发表于2017-02-20 14:46 被阅读237次

    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都可以监听键盘 和点击的事件

    相关文章

      网友评论

          本文标题:UIWindow知识归纳

          本文链接:https://www.haomeiwen.com/subject/sxqewttx.html