zoukankan      html  css  js  c++  java
  • iOS -- UIWindow的使用

    现在有一个需求:

      在判断出用户token过期,或者没有登录的时候显示登录界面

    这个需求实现起来有三个方法:

      1 改变UIApplication的window的根控制器

      2 在当前控制器present出来登录界面

      3 通过添加window显示登录界面

    前两种方法的弊端:

      第一种方法会销毁之前的控制器,登陆成功之后需要重新加载,可能无法回到登陆之前的界面

      第二种方法只能在控制器中实现,如果是在一个view中,就无法用这种方式实现

    综上所述,第三种方法是最通用,效率最高的

    关于UIWindow的介绍:

    一、UIWindow特点

    (1)UIWindow 是一种特殊的 UIView,通常在一个 app 中至少会有三个 UIWindow:

    • app delegate里的window
    • 状态栏的window(比较特殊,虽然在程序内部可以调用某些api显示隐藏或改变其UI,但它的window是不被我们的应用程序内部所持有的)
    • 键盘的window

    (2)iOS程序启动完毕后,创建的第一个视图控件就是 UIWindow,接着创建控制器的 view,最后将控制器的 view 添加到 UIWindow 上,于是控制器的 view 就显示在屏幕上了;

    (3)一个iOS程序之所以能显示到屏幕上,完全是因为它有 UIWindow,也就说,没有UIWindow,就看不见任何UI界面。

    二、添加 UIView 到 UIWindow 中两种常见方式:

    (1)- (void)addSubview:(UIView )view;*

    直接将 view 添加到 UIWindow 中,但并不会理会 view 对应的 UIViewController;

    (2)设置window的rootViewController;

    这种做法会自动将 rootViewController 的 view 添加到 UIWindow 中,是苹果推荐的做法

    三、UIWindow 常用方法

    - (void)makeKeyWindow;

    让当前 UIWindow 变成 keyWindow(主窗口);

    - (void)makeKeyAndVisible;

    让当前 UIWindow 变成 keyWindow,并显示出来。

    四、UIWindow 的获得

    [UIApplication sharedApplication].windows

    在本应用中打开的 UIWindow 列表,这样就可以接触应用中的任何一个 UIView 对象; 
    (平时输入文字弹出的键盘,就处在一个新的 UIWindow 中)

    [UIApplication sharedApplication].keyWindow    

    用来接收键盘以及非触摸类的消息事件的 UIWindow,而且程序中每个时刻只能有一个 UIWindow 是 keyWindow。如果某个 UIWindow 内部的文本框不能输入文字,可能是因为这个 UIWindow 不是 keyWindow。

    如果想通过这个方法改变keyWindow的rootviewcontroller来改变显示的界面,可能无法成功

    window,多个时,[UIApplication sharedApplication].windows[0]和[[UIApplication sharedApplication] delegate] window]获取的是最开始创建的window,而[UIApplication sharedApplication].keyWindow获取的是当前显示的window,不一定是最初的window

    正确的代码是

        UIWindow * win = [UIApplication sharedApplication].delegate.window;
        [win setRootViewController:vc];
        [win makeKeyAndVisible];
    

    五、UIWindow 层级

    self.window.windowLevel = UIWindowLevelAlert;

    UIWindow 有三个层级:UIWindowLevelAlert, UIWindowLevelStatusBar, UIWindowLevelNormal,

    Normal,StatusBar,Alert 分别 为 0,1000,2000

     windowLevel 是 CGFloat 类型,可以进行加减运算,或自定义优先级

    现在我们来解决问题:创建一个自己的window类,根控制器设置为登陆控制器,通过控制window的hidden属性来显示或隐藏,然后在需要的时候将window显示出来,登陆完成后隐藏

    注意:想要自己的window显示,就要保证windwo对象不能被销毁,需要保持在内存中,比较方便的做法是将window设置成单例,就可以保存在内存中,在不需要的时候设置为nil,释放内存

    #import <UIKit/UIKit.h>
    
    @interface LogInWindow : UIWindow
    + (instancetype)sharedLogInWindow;
    + (void)hideWindow;
    @end
    
    
    
    
    #import "LogInWindow.h"
    #import "LogInVC.h"
    static LogInWindow * __logWin;
    
    @implementation LogInWindow
    
    + (instancetype)sharedLogInWindow{
        
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            __logWin = [[LogInWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
            UIStoryboard * logSB = [UIStoryboard storyboardWithName:@"LogInSB" bundle:[NSBundle mainBundle]];
            UINavigationController * logNav = [logSB instantiateViewControllerWithIdentifier:@"LogInNav"];
            __logWin.rootViewController = logNav;
        });
        return __logWin;
    }
    
    + (void)hideWindow{
        
        [LogInWindow sharedLogInWindow].hidden = YES;
        
    }
    
    @end
    

    UIWindow使用需要注意的地方:

    切换根控制器可能会造成内存泄漏:如果present了一个控制器,在当前控制器切换rootViewController,而没有执行dismiss操作,就会导致控制器无法释放,造成内存泄漏

      在切换控制器之前需要执行dismiss操作

        [self dismissViewControllerAnimated:YES completion:^{
            //在这里更换根控制器
        }];
    

    获取最上层window的方法:

    - (UIWindow *)lastWindow
    {
        NSArray *windows = [UIApplication sharedApplication].windows;
        for(UIWindow *window in [windows reverseObjectEnumerator]) {
             
            if ([window isKindOfClass:[UIWindow class]] &&
                CGRectEqualToRect(window.bounds, [UIScreen mainScreen].bounds))
                 
                return window;
        }
         
        return [UIApplication sharedApplication].keyWindow;
    }
    

    https://www.jianshu.com/p/98cd7fc4bfba这个是大神写的,超级厉害

    https://yq.aliyun.com/articles/62456 这个阿里云的文章写的云里雾里,但是好像很牛逼的样子,先存起来

  • 相关阅读:
    android 启动报错
    android 百度地图
    android LayoutInflater使用
    spring mvc No mapping found for HTTP request with URI [/web/test.do] in DispatcherServlet with name 'spring'
    sql mysql和sqlserver存在就更新,不存在就插入的写法(转)
    jsp include
    json 解析
    css
    Scrapy组件之item
    Scrapy库安装和项目创建
  • 原文地址:https://www.cnblogs.com/chebaodaren/p/9548951.html
Copyright © 2011-2022 走看看