UIWindow

作者: 无邪8 | 来源:发表于2017-12-27 14:24 被阅读30次

    一、简介

    <<UIWindow类定义,管理和协调的Windows应用程序显示在屏幕上的对象(如Windows)。一个窗口的两个主要职能是,为显示其意见面积和分发活动的意见。窗口是在视图层次的根。一个窗口属于一个级别;一个级别的窗口出现另一个层面以上。例如,警报出现高于正常的窗口。通常情况下,只有一个在IOS应用程序的窗口

    <<UIWindow :UIWindow是一种特殊的UIView,通常在一个app中只会有一个UIWindow

    <<继承关系:UIWindow --> UIView -->UIResponder-->NSObject

    <<它包含了应用中的可见内容;

    <<它在视图和应用对象之间传递触摸事件中起很重要的作用;

    <<它和视图控制器配合完成方向转变

          在iOS系统中,windows没有标题栏、关闭框以及其他可视控件。一个window就是一个或多个视图的空白容器,而且应用不能通过window来改变自己的显示内容。当你想要改变现显示容的时候,改变最上层的视图就可以了。

        大多数iOS应用在其生命周期内只使用一个window,这个window从应用的主nib文件中加载,铺满整个主屏幕。当然,如果你的应用支持外部显示,它会额外创建一个window,用于外部显示。系统会创建其他典型的window,一般是在响应特殊事件时创建,例如来电显示。

    格式为

    1--> 设置根视图(属性的作用

    [self.window.rootviewcontroller=vc];;   (这是具体的例子

    @property(nullable, nonatomic,strong) UIViewController *rootViewController NS_AVAILABLE_IOS(4_0); // 设置根视图, 默认是空的   (这是属性的说明

    二、UIWindow的创建和配置

    可以代码或者InterfaceBuilder来创建并设置window。您在启动时创建了窗口,并应保留它,并将其保存到应用程序代理对象中的引用。如果需要额外的window,则在需要用的时候创建即可。

        创建window不需要考虑应用实在前台启动还是后台启动。创建和设置一个window不需要花太多资源。如果程序直接在后台启动,那么你就不能让window可见,直到window到前台之后再让其可见

    1、InterfaceBuilder创建UIWindow

    用IB创建window非常简单,因为Xcode的工程模板可以替你你创建。每个应用都会包含一个主XIB文件,这个XIB就包含了一个主window。另外,这些模板也为window在应用的代理对象中定义了outlet,你可以在代码中通过outlet取到window对象。

    注意:在使用IB创建window的时候,应该在属性设置栏中设置为全屏。如果没有设置为全屏,且window小于手机的屏幕尺寸,那么一些视图的触摸时间肯接收不到。这是因为window接受不到自己区域以外的触摸事件。如果视图的触摸点没在window的区域范围内,则响应不到触摸事件。所以要确保window是全屏。

        如果你重构代码时用到 IB添加window,也很简单,像XIB文件中拖一个window对象,然后进行如下操作:

    要在运行时访问window,应该把window和outlet相连。outlet一般情况下是在application delegate中,也可以是这个NIB文件对应的代码文件。

    重构过程中如果需要新建一个主NIB,那就得在info.plist文件中设置NSMainNibFil键。通过设置NSMainNibFil的值来确保这个window在代理方法application:didFinishLaunchingWithOptions:被调用是得到加载。

    2、纯代码创建window


    上面代码中的self.window,是在application delegate中已经声明过得属性,用来保存window对象。如果你创建的是用于外部显示的window,应该给它重新命名,并且需要设置bounds。

     创建window的时候,要把bounds设置为屏幕大小,不能有任何缩减

    三、将view添加到UIWindow

    1、直接将控制器的view添加到UIWindow中,并不理会它对应的控制器

    [self.window  addsubview:vc.view];

    直接将view通过addSubview方式添加到window中,程序负责维护view的生命周期以及刷新,但是并不会为去理会view对应的ViewController,因此采用这种方法将view添加到window以后,我们还要保持view对应的ViewController的有效性,不能过早释放。

    2、设置uiwindow的根控制器,自动将rootviewcontroller的view添加到window中,负责管理rootviewcontroller的生命周期

    [self.window.rootviewcontroller=vc];

    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上面去。因为,难以管理。

    四、获取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

    五、UIWindow的视图属性(属性的顺序与苹果API一致)

    1-->设置Screen

            self.window.screen = self.externalScreen;  

    @property(nonatomic,strong) UIScreen *screen NS_AVAILABLE_IOS(3_2);//默认是[UIScreen mainScreen]。改变屏幕可能是一个昂贵的操作,不应该在性能敏感的代码中完成

    2-->设置视图层级

    self.window.windowLevel = UIWindowLevelAlert+1;

    @property(nonatomic) UIWindowLevel windowLevel; // 默认为0

    <注意>UIWindow在显示的时候会根据UIWindowLevel进行排序的,即Level高的将排在所有Level比他低的层级的前面。下面我们来看UIWindowLevel的定义:

                    const   UIWindowLevel UIWindowLevelNormal; 默认为0    

        const UIWindowLevel UIWindowLevelAlert;默认为2000

        const UIWindowLevel UIWindowLevelStatusBar;默认为1000

        typedef CGFloat UIWindowLevel;

    Normal ,StatusBar,Alert.输出他们三个层级的值,我们发现从左到右依次是0,1000,2000,也就是说Normal级别是最低的,StatusBar处于中级,Alert级别最高。而通常我们的程序的界面都是处于Normal这个级别的,系统顶部的状态栏应该是处于StatusBar级别,提醒用户等操作位于Alert级别。根据window显示级别优先原则,级别高的会显示在最上层,级别低的在下面,我们程序正常显示的view在最底层;

    3-->是否为根视图(只读属性)

    BOOL  isKeyWindow=self.window.keyWindow;

    @property(nonatomic,readonly,getter=isKeyWindow) BOOL keyWindow;

    4-->becomeKeyWindow

    - (void)becomeKeyWindow; //该方法不应该被手动调用,当window变为keyWindow时会被自动调用来通知window。可以继承UIWindow重写此方法来实现功能

    5-->resignKeyWindow

    - (void)resignKeyWindow; // 该方法不应该被手动调用,当window不再是keyWindow时(例如其他window实例调用了- makeKeyWindow或- makeKeyAndVisible方法)会被自动调用来通知window。可以继承UIWindow重写此方法来实现功能。

    6-->让当前UIWindow变成keyWindow,默认不显示

      [self.window makeKeyWindow];

    - (void)makeKeyWindow; // 让window成为keyWindow(主窗口),默认不可见

    7-->让当前UIWindow变成keyWindow,并显示出来

     [self.window makeKeyAndVisible];

    - (void)makeKeyAndVisible;//  让当前UIWindow变成keyWindow,并显示出来

    8-->设置uiwindow的根控制器

    [self.window.rootviewcontroller=vc];

    @property(nullable, nonatomic,strong) UIViewController *rootViewController NS_AVAILABLE_IOS(4_0);//  根控制器

    9-->分发自定义事件

    UIApplication 和 UIWindow 有方法 - sendEvent: ,用于把事件分发到 hitTest View

    UIApplication == sendEvent: ==> UIWindow == sendEvent: ==> hitTest View

    - (void)sendEvent:(UIEvent *)event;//UIApplication调用window的该方法给window分发事件,window再将事件分发到合适的目标,比如将触摸事件分发到真正触摸的view上。可以直接调用该方法分发自定义事件。

    10-->把该window中的一个坐标转换成在目标window中时的坐标值

    CGPoint p = [self.window1 convertPoint:CGPointMake(0, 0) toWindow:self.window0];

    - (CGPoint)convertPoint:(CGPoint)point toWindow:(nullable UIWindow *)window;

    11-->把目标window中的一个坐标转换成在该window中时的坐标值

    CGPoint p = [self.window1 convertPoint:CGPointMake(0, 0) fromWindow:self.window0];

    - (CGPoint)convertPoint:(CGPoint)point fromWindow:(UIWindow *)window;

    12-->把该window中的一个矩阵转换成在目标window中时的矩阵值

    CGRect rect=[self.window convertRect:CGRectMake(0, 0, 0, 0) toView:self.window0];

    - (CGRect)convertRect:(CGRect)rect toWindow:(UIWindow *)window;  

    13--> 把目标window中的一个矩阵转换成在该window中时的矩阵值

     CGRect rect=[self.window convertRect:CGRectMake(0, 0, 0, 0) toView:self.window0];

    - (CGRect)convertRect:(CGRect)rect fromWindow:(UIWindow *)window;

    六、UIWindow的常量属性

    1-->UIWindowLevel的枚举

    UIWindowLevelNormal;// 0.000000

    UIWindowLevelStatusBar;// 1000.000000

    UIWindowLevelAlert;// 2000.000000

    2-->监测window的通知名称:

    UIKIT_EXTERN NSString *const UIWindowDidBecomeVisibleNotification; // 当window激活时并展示在界面的时候触发,返回空

    UIKIT_EXTERN NSString *const UIWindowDidBecomeHiddenNotification;  // 当window隐藏的时候触发,暂时没有实际测,返回空

    UIKIT_EXTERN NSString *const UIWindowDidBecomeKeyNotification;    // 当window被设置为keyWindow时触发,返回空

    UIKIT_EXTERN NSString *const UIWindowDidResignKeyNotification;    // 当window的key位置被取代时触发,返回空

    3-->监测键盘的通知名称:

    UIKIT_EXTERN NSString *const UIKeyboardWillShowNotification;// 显示键盘的时候立即发出该通知

    UIKIT_EXTERN NSString *const UIKeyboardDidShowNotification;//显示键盘后才发出该通知

    UIKIT_EXTERN NSString *const UIKeyboardWillHideNotification;//键盘即将消失的时候立即发出该通知

    UIKIT_EXTERN NSString *const UIKeyboardDidHideNotification;//键盘消失后才发出该通知

    UIKIT_EXTERN NSString *const UIKeyboardWillChangeFrameNotification  NS_AVAILABLE_IOS(5_0);//键盘的frame值发生变化的时候立即发出该通知

    UIKIT_EXTERN NSString *const UIKeyboardDidChangeFrameNotification  NS_AVAILABLE_IOS(5_0);//键盘的frame值发生变化后才发出该通知

    4-->userInfo字典中key为:

    NSString *const UIKeyboardFrameBeginUserInfoKey;//userInfo字典里该key对应一个NSValue,存储一个包含键盘初始frame值的CGRect结构(即键盘刚出现时的frame值)

    NSString *const UIKeyboardFrameEndUserInfoKey;//userInfo字典里该key对应一个NSValue,存储一个包含键盘结束frame值的CGRect结构(即键盘动画结束后的frame值)

    NSString *const UIKeyboardAnimationDurationUserInfoKey;//userInfo字典里该key对应一个NSNumber,存储一个包含键盘进入或离开屏幕的UIViewAnimationCurve结构

    NSString *const UIKeyboardAnimationCurveUserInfoKey;//userInfo字典里该key对应一个NSNumber,存储一个包含键盘动画时间的double值,时间以秒为单位。

    例如,在UIKeyboardWillShowNotification,UIKeyboardDidShowNotification通知中的userInfo内容为

    userInfo = {

        UIKeyboardAnimationCurveUserInfoKey = 0;

        UIKeyboardAnimationDurationUserInfoKey = "0.25";

        UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}";

        UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 588}";

        UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 372}";

        UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 480}, {320, 216}}";

        UIKeyboardFrameChangedByUserInteraction = 0;

        UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 264}, {320, 216}}";

    }

    参考:

    IOS: iPhone键盘通知与键盘定制

    UIKit-UIWindow详解

    UIWindow 详解及使用场景

    【iOS】UIWindow中文详解

    IOS学习记录 基础类UIWindow,UIView,UISreen篇

    相关文章

      网友评论

      • 宇文袥:感谢楼主,写得很详细,很有逻辑
        无邪8:@宇文袥 谢谢

      本文标题:UIWindow

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