UIView的功能
負責渲染區域的內容,并且響應該區域內發生的觸摸事件
UIWindow
在iOS App中,UIWindow是最頂層的界面內容,我們使用UIWindow和UIView來呈現界面。UIWindow并不包含任何默認的內容,但是它被當作UIView的容器,用于放置應用中所有的UIView。
從繼承關系來看,UIWindow繼承自UIView,所以UIWindow除了具有UIView的所有功能之外,還增加了一些特有的屬性和方法,而我們最常用的方法,就是在App剛啟動時,調用UIWindow的rootViewController(必須指定根控制器) 和 makeKeyAndVisible方法
狀態欄和鍵盤都是特殊的UIWindow。
UIWindow在程序中主要起到三個作用:
1、作為容器,包含app所要顯示的所有視圖
2、傳遞觸摸消息到程序中view和其他對象
3、與UIViewController協同工作,方便完成設備方向旋轉的支持
通常我們可以采取兩種方法將view添加到UIWindow中:
1、addSubview
直接將view通過addSubview方式添加到window中,程序負責維護view的生命周期以及刷新,但是并不會為去理會view對應的ViewController,因此采用這種方法將view添加到window以后,我們還要保持view對應的ViewController的有效性,不能過早釋放。
2、rootViewController
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上面去。因為,難以管理。
WindowLevel
UIWindow的層級由一個UIWindowLevel類型屬性windowLevel,該屬性指示了UIWindow的層級,windowLevel有三種可取值。
并且層級是可以做加減的self.window.windowLevel = UIWindowLevelAlert+1;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal; //默認,值為0
UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert; //值為2000
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar ; // 值為1000
Normal ,StatusBar,Alert.輸出他們三個層級的值,我們發現從左到右依次是0,1000,2000,也就是說Normal級別是最低的,StatusBar處于中級,Alert級別最高。而通常我們的程序的界面都是處于Normal這個級別的,系統頂部的狀態欄應該是處于StatusBar級別,提醒用戶等操作位于Alert級別。根據window顯示級別優先原則,級別高的會顯示在最上層,級別低的在下面,我們程序正常顯示的view在最底層;
如何獲取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
四大對象的關系圖
keyWindow
?當前app可以打開的多個window 如系統狀態欄其實就是一個window ,程序啟動的時候創建的默認的window ,彈出鍵盤也是一個window ,alterView 彈框也是window 。但是keyWindow只有一個 ,一般情況下就是我們程序啟動時設置的默認的window
官方文檔中是這樣解釋的 “The key window is the one that is designated to receive keyboard and other non-touch related events. Only one window at a time may be the key window." 翻譯過來就是說,keyWindow是指定的用來接收鍵盤以及非觸摸類的消息,而且程序中每一個時刻只能有一個window是keyWindow。
問題一:一個應用程序只能有一個主窗口,如果程序中創建了兩個Window,那么誰是主窗口?
?①iOS 7 以后,主窗口和次窗口是沒有區別的
②iOS 7 之前,如果后面的窗口設置為主窗口,會把之前設置的主窗口覆蓋掉?
問題二:只有主窗口才能響應鍵盤的輸入事件?
在ios9.3的模擬器中,主窗口和非主窗口中的輸入框都能輸入文字,但是在ios6.1的模擬器中,非主窗口的輸入框不能輸入文字。? 獲取keyWindow的方式
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
UIViewController *rootViewController = keyWindow.rootViewController;?
注意:keyWindow不是一成不變的,當你創建alertView或者ActionSheet的時候,它們所在的window會變成keyWindow。也就是說系統默認創建的window首先變成keywindow,而當彈框的時候,alertView所在的window變成keywindow,默認的keywindow變成非keywindow。
?@property(nonatomic,readonly) NSArray? *windows;在windows數組里面,window是根據windowLevel來排列的,最后一個覆蓋在最上面。這里的windows數組不包括系統提供的window,比如說狀態欄就是在一個系統創建的window里面。
?測試代碼如下:
#import "AppDelegate.h"
@interface AppDelegate (
)@property(strong, nonatomic) UIWindow *normalWindow;
@property(strong, nonatomic) UIWindow *coverStatusBarWindow;
@property(strong, nonatomic) UIWindow *alertLevelWindow;
@end
@implementation AppDelegate
- (void)coverWindowOnClicked{
? ? NSLog(@"tap tap 11111");? ?
?[[NSNotificationCenter defaultCenter]postNotificationName:@"kOnClickedStatusBarNotification" object:self userInfo:nil];}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event{
NSLog(@"touchesBegan touchesBegan55555555555");
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//1.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor yellowColor];
self.window.rootViewController = [[UIViewController alloc]init];
[self.window makeKeyAndVisible];
NSLog(@"1hahah%f",[UIApplication sharedApplication].keyWindow.windowLevel);
//2.
UIWindow *normalWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
normalWindow.backgroundColor = [UIColor grayColor];
normalWindow.windowLevel = UIWindowLevelNormal;
normalWindow.rootViewController = [[UIViewController alloc]init];
[normalWindow makeKeyAndVisible];
self.normalWindow = normalWindow;
UITextField *tf = [[UITextField alloc] init];
tf.frame = CGRectMake(10, 64, 100, 20);
tf.borderStyle = UITextBorderStyleRoundedRect;
[self.normalWindow addSubview:tf];
UITapGestureRecognizer *tap1 = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(coverWindowOnClicked)];
[self.normalWindow addGestureRecognizer:tap1];
NSLog(@"2hahah%f",[UIApplication sharedApplication].keyWindow.windowLevel);
//2. 創建覆蓋著狀態欄的window
UIWindow * coverStatusBarWindow =[[UIWindow alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 20)];
coverStatusBarWindow.rootViewController = [[UIViewController alloc]init];
coverStatusBarWindow.backgroundColor = [UIColor redColor];
//級別要比 狀態欄的級別高
coverStatusBarWindow.windowLevel = UIWindowLevelStatusBar+1;
[coverStatusBarWindow makeKeyAndVisible];
self.coverStatusBarWindow = coverStatusBarWindow;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(coverWindowOnClicked)];
[self.coverStatusBarWindow addGestureRecognizer:tap];
//想移除coverStatusBarWindow 將其賦值為空
//? ? self.coverStatusBarWindow = nil;
// 3.創建UIwindow1
self.alertLevelWindow = [[UIWindow alloc] initWithFrame:CGRectMake(50, 150, 200, 250)];
self.alertLevelWindow.backgroundColor = [UIColor blueColor];
UIViewController *vc1 = [[UIViewController alloc] init];
self.alertLevelWindow.rootViewController = vc1;
self.alertLevelWindow.windowLevel = UIWindowLevelAlert;
[self.alertLevelWindow makeKeyAndVisible];
// 給UIwindow1添加一個輸入框
UITextField *tf1 = [[UITextField alloc] init];
tf1.frame = CGRectMake(10, 64, 100, 20);
tf1.borderStyle = UITextBorderStyleRoundedRect;
[self.alertLevelWindow addSubview:tf1];
UITapGestureRecognizer *tap2 = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(coverWindowOnClicked)];
[self.alertLevelWindow addGestureRecognizer:tap2];
NSLog(@"1hahah%f",[UIApplication sharedApplication].keyWindow.windowLevel);
NSLog(@"windows 剛啟動 ---%@",[UIApplication sharedApplication].windows);
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyBoardShow:) name:UIKeyboardDidShowNotification object:nil];
return YES;
}
- (void)keyBoardShow:(NSNotification *)notif{
NSLog(@"windows 鍵盤彈出 ---%@",[UIApplication sharedApplication].windows);
NSLog(@"2hahah%f",[UIApplication sharedApplication].keyWindow.windowLevel);
}
可以得出以下結論: (實踐是檢驗真理的唯一標準 網上有很多是錯誤的還是自己實踐最好)
1) 同一層級的 最后一個顯示出來,上一個被覆蓋
2)UIWindow在顯示的時候是不管KeyWindow是誰,都是Level優先的,即Level最高的始終顯示在最前面。
3)誰最后設置的 makeKeyAndVisible 誰就是keyWindow 其他的也會顯示出來 所有的window都可以監聽鍵盤 和點擊的事件