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 這個阿里雲的文章寫的雲里霧里,但是好像很牛逼的樣子,先存起來

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM