UIWindow的windowLevel属性

作者: 踩坑小分队 | 来源:发表于2016-10-14 10:21 被阅读5132次

    UIWindow是我们必不可少的类,可能使用的较少,但是一个项目中少它不行,因为它是我们最顶层的界面容器,我们自己的相关的ViewController都要最终放到window.rootController中。一班情况UIWindow作为顶层容器的存在。
    介绍下UIWindow的一个属性,windowLevel。
    苹果文档中windowLevel有三种形式,并且是CGFloat类型的

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

    它们的值可以分别通过打印来验证

    NSLog(@"%f  %f  %f",UIWindowLevelNormal,UIWindowLevelAlert,UIWindowLevelStatusBar);
    

    0.000000 2000.000000 1000.000000
    这个属性是干啥的

    属性等级.png
    也就是可以通过这个属性来判断是不是优先显示UIWindow。默认的window的windowLevel是UIWindowLevelNormal,也就是0;
    下面我们通过小Demo进行验证
    1、首先验证苹果头文件中的UIWindow的windowLevel是多少
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    NSLog(@"UIWindowLevelNormal = %f  UIWindowLevelAlert = %f  UIWindowLevelStatusBar = %f  appDelegate.window.windowLevel = %f",UIWindowLevelNormal,UIWindowLevelAlert,UIWindowLevelStatusBar,appDelegate.window.windowLevel);
    

    验证结果

    UIWindowLevelNormal = 0.000000  UIWindowLevelAlert = 2000.000000  UIWindowLevelStatusBar = 1000.000000  appDelegate.window.windowLevel = 0.000000
    

    2、验证系统通过UIWindow的windowLevel大小不同优先显示不同的UIWindow
    需要注意的是,UIWindow的创建和UIView不同,UIWindow一旦被创建他就会添加到整个界面上。
    如果你创建的UIWindow没有显示,请参考:
    http://www.jianshu.com/p/75befce85623

    创建不同的windowLevel查看相关的效果,代码如下

    #import "ViewController.h"
    @interface ViewController ()
    // 创建属性
    @property (nonatomic, strong)UIWindow *myWindow1;
    @property (nonatomic, strong)UIWindow *myWindow2;
    @property (nonatomic, strong)UIWindow *myWindow3;
    @end
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        // 创建测试按钮
        UIButton *tempBtn = [UIButton buttonWithType:UIButtonTypeSystem];
        tempBtn.frame = CGRectMake(100, 100, 100, 100);
        tempBtn.backgroundColor = [UIColor cyanColor];
        // 通过按钮的点击事件生成不同windowLevel级别的window
        [tempBtn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:tempBtn];
    }
    

    按钮的单击事件调用test1
    创建一个比默认window的windowLevel大的window来看一下什么效果,效果是会盖在原来的window上面

    /**
     *  这个方法证明两个问题
     *1、创建 window 不用添加到任何的控件上面,直接创建完毕就能添加
     *2、创建一个比默认window的windowLevel大的window来看一下什么效果,效果是会盖在原来的window上面
     */
    - (void)test1
    {
        // 创建window
        if (self.myWindow1 == nil) {
            self.myWindow1 = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
            
            UIButton *windowBtn = [UIButton buttonWithType:UIButtonTypeCustom];
            [windowBtn setTitle:@"window1点我隐藏" forState:UIControlStateNormal];
            windowBtn.backgroundColor = [UIColor redColor];
            windowBtn.frame = CGRectMake(100, 300, 100, 100);
            [windowBtn addTarget:self action:@selector(clickWindowBtn:) forControlEvents:UIControlEventTouchUpInside];
            [self.myWindow1 addSubview:windowBtn];
            
        }
        // 设置window的颜色,这里设置成半透明的,方便查看window的层级关系
        self.myWindow1.backgroundColor = [UIColor colorWithRed:0.00f green:1.00f blue:0.01f alpha:0.50f];
        // 设置 window 的 windowLevel
        self.myWindow1.windowLevel = 100;
        self.myWindow1.hidden = NO;
    }
    

    效果图:


    Paste_Image.png

    按钮的单击事件调用test2
    创建一个和默认window的windowLevel一样大的window来看一下什么效果,效果是会盖在原来的window上面

    /**
     *  这个方法证明两个问题
     *1、创建 window 不用添加到任何的控件上面,直接创建完毕就能添加
     *2、创建一个和默认window的windowLevel一样大的window来看一下什么效果,效果是会盖在原来的window上面
     */
    - (void)test2
    {
        // 创建window
        if (self.myWindow2 == nil) {
            self.myWindow2 = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
            
            UIButton *windowBtn = [UIButton buttonWithType:UIButtonTypeCustom];
            [windowBtn setTitle:@"window2点我隐藏" forState:UIControlStateNormal];
            windowBtn.backgroundColor = [UIColor greenColor];
            windowBtn.frame = CGRectMake(100, 300, 200, 100);
            [windowBtn addTarget:self action:@selector(clickWindow2Btn:) forControlEvents:UIControlEventTouchUpInside];
            [self.myWindow2 addSubview:windowBtn];
            
        }
        // 设置window的颜色,这里设置成半透明的,方便查看window的层级关系
        self.myWindow2.backgroundColor = [UIColor colorWithRed:0.91f green:0.13f blue:0.13f alpha:0.50f];
    
        // 设置 window 的 windowLevel,设置的和当前存在的window一样
        self.myWindow2.windowLevel = self.view.window.windowLevel;
        self.myWindow2.hidden = NO;
        [self.myWindow2 makeKeyAndVisible];
    }
    

    效果图


    Paste_Image.png

    按钮的单击事件调用test3
    创建一个和默认window的windowLevel一样大的window来看一下什么效果,效果是会在在原来的window下面

    /**
     *  这个方法证明两个问题
     *1、创建 window 不用添加到任何的控件上面,直接创建完毕就能添加
     *2、创建一个和默认window的windowLevel一样大的window来看一下什么效果,效果是会在在原来的window下面
     */
    
    - (void)test3
    {
        // 为了展示相关的效果,把当前的window的透明度设置为0.5
        self.view.window.alpha = 0.5;
        
        // 创建window
        if (self.myWindow3 == nil) {
            self.myWindow3 = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        }
        // 设置window的颜色,这里设置成半透明的,方便查看window的层级关系
        self.myWindow3.backgroundColor = [UIColor blueColor];
        
        // 设置 window 的 windowLevel,设置的比当前存在的window的小
        self.myWindow3.windowLevel = -1;
        self.myWindow3.hidden = NO;
        [self.myWindow3 makeKeyAndVisible];
    }
    

    执行效果:


    Paste_Image.png

    调用完test方法可以查看当前一共有几个window,并且查看当前的测试window有没有实例化成功,所有的实例化的window并且声明周期够长,可以在[UIApplication sharedApplication].windows找到。

    // 查看当前所有的window
    NSLog(@"当前所有的window = %@ \nself.myWindow1 = %@",[UIApplication sharedApplication].windows,self.myWindow3);
    

    通过以上总结如下:
    1、UIWindowLevel的值不仅仅只有UIWindowLevelNormal、UIWindowLevelAlert、UIWindowLevelStatusBar 这三个,可以通过test3看出,只要你想可以是随意值,负数都可以。
    2、UIWindow的显示的确可以通过UIWindowLevel来区分优先级,所有的window都会被加在界面上,只不过会通过优先级罗列起来,UIWindowLevel大的在上面显示,UIWindowLevel小的在下面显示。
    3、UIWindowLevel优先级相等的情况下,看谁后实例化了,谁后实例化谁先显示

    说了这么多了,有啥应用场景吗?
    我想到的有如下场景
    1、可以看到蚂蚁聚宝app每次压入后台的时候上面都有一层毛玻璃,我觉得可以用一个window搞一下。
    2、其实系统的UIAlertView弹框的显示就是在自己创建的一个Window上面显示的<可以用alertView.window和当前的window比较,发现不是一个window,并且alertView.window.windowLevel大于0>,那么自定义的弹框或者其他也可以通过这个思路来搞一下。并且可以随时随地的销毁获取。
    3、或者说每次有单独显示的界面也可以搞个window

    代码链接参考:https://github.com/RunOfTheSnail/UIWindowDemo001

    以上是我自己根据相关的运行效果总结的,如果有哪位大神觉得有地方描述的不准确,欢迎指正哈,在下感激不尽!!!
    还有其他的应用的话欢迎补充。

    相关文章

      网友评论

      • 默默_David:自建的window如何释放呢?我使用[win resignKeyWindow]没用,改变windowlevel也仅仅是改变显示顺序,[UIApplication sharedApplication].windows数组里面仍然有这个数组。楼主你这个window是强引用在ViewController里面的,Viewcontroller释放了自然就释放,但是有代码控制使得window释放的办法吗?(ARC下)
        踩坑小分队:@默默_David 简单暴力一点,你写个通知,想要把那个Window干掉的时候,在你的Window创建的VC中,直接把这个Window置成nil。亲测可以


        removeWindow,这个方法就是你在D中发出的通知方法,在创建Window的VC中响应。

        - (void)removeWindow
        {

        self.myWindow3.hidden = YES;
        self.myWindow3 = nil;


        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"\n\n\n\n 333当前所有的window = %@ \nself.myWindow1 = %@",[UIApplication sharedApplication].windows,self.myWindow3);
        });

        }



        你可以试一下。
        默默_David:@踩坑小分队 没用啊,自建的window根本没有父视图啊,比如我创建一个window,屏幕大小,然后设置他的根视图控制器为控制器A,在A控制器中会有切换这个window的控制器为B的操作,在B中会有切换根视图控制器为C的操作……,这样的话,比如切换到D,在D中,如何释放点这个window并返回A的上一个页面?
        踩坑小分队:removeFromSuperview 不管用吗?Window的父类就是UIView嘛。
      • 058ff34b7220:请教一下,官方文档中对UIWindowLevelStatusBar的描述为:“The level for a status window. Windows at this level appear on top of windows at the UIWindowLevelAlert level.”,而对UIWindowLevelAlert的描述为:“The level for an alert view. Windows at this level appear on top of windows at the UIWindowLevelNormal level.”,之前看到很多文章代码验证的是Alert级别最高,而官方文档描述的是StateBar在Alert之上,这会不会互相矛盾?:scream:
        踩坑小分队:@DanbLee 牛逼!!学习了
        058ff34b7220:@踩坑小分队 后来去stackoverflow看了,确实是文档的问题:joy: https://stackoverflow.com/questions/44676406/uiwindow-with-alert-level-is-on-top-of-status-bar-level
        踩坑小分队:不好意思,回复晚了,我看了一下文档,的确是你说的那个样子的。但是实际的代码运行情况真的是和他的文档说明是不统一的。我也懵逼了。哈哈,知道原因了,随时交流。
      • LD_左岸:大神您看这个window的Level的等级和获取根控制器是否有关系 比如我的代码这么写 是不是一定是对的?
        +(UIViewController*)getRootViewController
        {
        UIWindow *window=[[UIApplication sharedApplication]keyWindow];
        if (window.windowLevel != UIWindowLevelNormal)
        {
        NSArray *windows = [[UIApplication sharedApplication] windows];
        for(UIWindow * tmpWin in windows)
        {
        if (tmpWin.windowLevel == UIWindowLevelNormal)
        {
        window = tmpWin;
        break;
        }
        }
        }
        UINavigationController *nc=(UINavigationController*) window.rootViewController;
        return nc.visibleViewController;

        }
        得到根控制器 如果Level有负数的话 这样得到的能是最下面的window吗
        踩坑小分队:你是想通过等级获得最下面的window吗?但是window的等级是可以手动设置的,你可以设置两个window的等级都是一样的。你想获取最下面的window进而获取rootVC的话,我建议还是通过appdelegate获取是最靠谱的。
        http://www.jianshu.com/p/ae84cd31d8f0,你看下这个。看看能不能解决你的问题。
      • 啊啊啊啊锋:不错!
        踩坑小分队:@啊啊啊啊锋 :smile::smile::smile::smile:
      • soullink2011:不错不错,:clap::clap::clap:

      本文标题:UIWindow的windowLevel属性

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