UIWindow詳解
前言
最近在做一個通知彈框的需求,應用到了UIWindow,之前沒有研究過,趁著這次機會了解下UIWindow。簡書上發現一篇好文章,特轉載過來,感謝作者。
作者:xx_cc連結:http://www.jianshu.com/p/af2a6a438a0a
應用內通知的一個demo: https://github.com/terryworona/TWMessageBarManager
1.UIWindow簡介
1、UIWindow是一種特殊的UIView,通常在一個app中至少會有一個UIWindow。
2、iOS程式啟動完畢後,建立的第一個檢視控制元件就是UIWindow,接著建立控制器的View,最後將控制器的View新增到UIWindow上,於是控制器的View就顯示在螢幕上了。
3、一個iOS程式之所以能顯示在螢幕上,完全是因為它有UIWindow,也就是說,沒有UIWindow就看不到任何UI介面。
4、狀態列和鍵盤都是特殊的UIWindow。
那麼UIWindow是如何將View顯示到螢幕上的呢
這裡有三個重要的物件UIScreen,UIWindow,UIView。
UIScreen物件識別物理螢幕連線到裝置
UIWindow物件提供繪畫支援給螢幕
UIView執行繪畫,當視窗要顯示內容的時候,UIView繪畫出他們的內容並附加到視窗上。
這樣View就顯示在視窗上了
2.UIWindow的建立
1.UIWindow是什麼時候建立的?
我們可以發現,當我們新建一個專案,直接在stroyboard為view設定一個背景顏色,然後執行專案,就能看到換了背景顏色的view,這說明系統已經幫我們建立了一個UIWindow,那麼這個UIWindow是什麼時候建立的?
我們找到程式的入口main
函式,來看程式的啟動過程
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
此時我們可以根據UIApplicationMain函式了解程式啟動的過程
1、根據傳遞的類名建立UIApplication物件,這是第一個物件
2、建立UIApplication代理物件,並給UIApplicaiton物件設定代理
3、開啟主執行迴圈 main events loop處理事件,保持程式一直執行
4、載入info.plist,判斷是否指定mian(xib 或者 storyboard)如果指定就去載入
當我們把指定的Main Interface 中mian給刪除的時候,重新執行程式,就會發現我們之前設定的view沒有辦法顯示了。
此時我們基本可以想到,UIWindow應該是在載入storyboard的時候系統建立的,那麼系統是如何載入storyboard的呢?
系統在載入storyboard的時候會做以下三件事情
1、建立視窗
2、載入mian.storyboard 並例項化view controller
3、分配新檢視控制器到視窗root viewcontroller,然後使視窗顯在示螢幕上。
因此,當系統載入完info.plist,判斷後發現沒有main,就不會載入storyboard,也就不會幫我們建立UIWindow,那麼我們需要自己在程式啟動完成的時候也就是在didFinishLaunchingWithOptions
方法中建立。
2.如何建立UIWindow?
首先根據系統載入storyboard時做的三件事情,我們可以總結出UIWindow建立步驟
1、建立視窗物件
2、建立視窗的根控制器,並且賦值
3、顯示視窗
並且我們在AppDelegate.h
中發現屬性window
@property (strong, nonatomic) UIWindow *window;
那麼我們來看一下如何建立UIWindow
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//建立視窗物件
self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
//建立視窗的根控制器,並且賦值
UIViewController *rootVc = [[UIViewController alloc]init];
self.window.rootViewController = rootVc;
//顯示視窗
[self.window makeKeyAndVisible];
return YES;
}
視窗顯示注意點:
1、我們看到系統為我們建立的window屬性是strong強引用,是為了不讓視窗銷燬,所以需要強引用
2、視窗的尺寸必須設定,一般設定為螢幕大小。
3、[self.window addsubview:rootVc.view];
可直接將控制器的view新增到UIWindow中,並不理會它對應的控制器,但是這種方法違背了MVC原則,當我們需要處理一些業務邏輯的時候就很麻煩了。
4、當發生螢幕旋轉事件的時候,UIapplication物件會將旋轉事件傳遞給UIWindow,UIWindow又會將旋轉事件傳遞給它的根控制器,由根控制器決定是否需要旋轉。UIapplication物件 -> UIWindow -> 根控制器。([self.window addsubview:rootVc.view];
沒有設定根控制器,所以不能跟著旋轉)。
5、設定根控制器可以將對應介面的事情交給對應的控制器去管理。
那麼[self.window makeKeyAndVisible];
這個方法為什麼就能顯示視窗呢?我們來看一下[self.window makeKeyAndVisible];
的底層實現了哪些功能
1、可以顯示視窗
2、成為應用程式的主視窗
當我們不呼叫這個方法,列印self.window。
UIWindow: 0x7f920503cc80; frame = (0 0; 414 736); hidden = YES; gestureRecognizers = <NSArray: 0x7f92050332a0>; layer = <UIWindowLayer: 0x7f920503ad50>>
我們可以看到hidden = YES
;那麼hidden = NO
就可以顯示視窗了另外,我們在[self.window makeKeyAndVisible]
;前後分別輸出一下application.keyWindow
NSLog(@"%@",application.keyWindow);
[self.window makeKeyAndVisible];
NSLog(@"%@",application.keyWindow);
列印內容
UIWindow[6259:1268399] (null)
UIWindow[6259:1268399] <UIWindow: 0x7fefdb529b30; frame = (0 0; 414 736); gestureRecognizers = <NSArray: 0x7fefdb529e40>; layer = <UIWindowLayer: 0x7fefdb529ae0>>
我們可以看到呼叫[self.window makeKeyAndVisible];
方法之後application.keyWindow
就有值了,那麼[self.window makeKeyAndVisible];
的底層實現就很明顯了。
1、可以顯示視窗
self.window.hidden = NO;
2、成為應用程式的主視窗application.keyWindow = self.window
,這個會報錯,因為application.keyWindow
是readonly,所以我們沒有辦法直接賦值。
3.通過storyboard載入控制器
剛才我們提到過系統在載入storyboard的時候會做以下三件事情
1、建立視窗
2、載入mian.storyboard 並例項化view controller
3、分配新檢視控制器到視窗root viewcontroller,然後使視窗顯在示螢幕上。
那麼我們用程式碼來模擬實現一下
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 1.建立視窗
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 2.載入main.storyboard,建立main.storyboard描述的控制器
// UIStoryboard專門用來載入stroyboard
// name:storyboard名稱不需要字尾
UIStoryboard *stroyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
// 載入sotryboard描述的控制器
// 載入箭頭指向的控制器
UIViewController *vc = [stroyboard instantiateInitialViewController];
//根據繫結標識載入
//UIViewController *vc = [stroyboard instantiateViewControllerWithIdentifier:@"red"];
// 設定視窗的根控制器
self.window.rootViewController = vc;
// 3.顯示視窗
[self.window makeKeyAndVisible];
return YES;
}
4.通過xib載入控制器
通過xib載入控制器和通過storyboard載入控制器類似,直接上程式碼
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 建立視窗的根控制器
// 通過xib建立控制器
ViewController *vc = [[ViewController alloc] initWithNibName:@"VC" bundle:nil];
//vc.view.backgroundColor = [UIColor redColor];
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];
return YES;
}
3.UIWindow的層級
UIWindow是有層級的,層級高的顯示在最外面,當層級相同時,越靠後呼叫的顯示在外面。
UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal; //預設,值為0
UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert; //值為2000
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar ; // 值為1000
所以UIWindowLevelNormal < UIWindowLevelStatusBar< UIWindowLevelAlert
並且層級是可以做加減的self.window.windowLevel = UIWindowLevelAlert+1;
關於UIApplication的介紹可以看這篇文章iOS-UIApplication詳解
相關文章
- UIView && UIWindowUIView
- iOS-UIKit框架學習—UIWindowiOSUI框架
- UIWindow的rootViewController的問題UIViewController
- 建立 UIWindow 被忽視的一個坑UI
- 防止 UIWindow 延遲釋放佔用狀態列UI
- iOS初始化UIWindow並且設定級別iOSUI
- 【iOS開發】防止UIWindow延遲釋放佔用狀態列iOSUI
- http協議/cookie詳解/session詳解HTTP協議CookieSession
- Lombok 註解詳解Lombok
- Java註解詳解Java
- Java 註解詳解Java
- Java註解最全詳解(超級詳細)Java
- HiveQL詳解Hive
- 詳解Inode
- Vuex詳解Vue
- PWA詳解
- 詳解CountDownLatchCountDownLatch
- DiffUtil詳解
- iptables詳解
- TCP詳解TCP
- CDN詳解
- Typescript詳解TypeScript
- Mybatis詳解MyBatis
- Synchronized詳解synchronized
- TLS 詳解TLS
- 詳解bind
- 詳解GOPATHGo
- HTTP 詳解HTTP
- JavaScript this詳解JavaScript
- BT詳解
- nginx 詳解Nginx
- @autowired詳解
- ECharts 詳解Echarts
- DiskBasedCache詳解
- JavaWeb詳解JavaWeb
- IndexedDB詳解Index
- BART詳解
- JDBC詳解JDBC