每一個IOS程序都有一個UIWindow,在我們通過模板簡歷工程的時候,xcode會自動幫我們生成一個window,然后讓它變成keyWindow並顯示出來。這一切都來的那么自然,以至於我們大部分時候都忽略了自己也是可以創建UIWindow對象。
通常在我們需要自定義UIAlertView的時候(IOS 5.0以前AlertView的背景樣式等都不能換)我們可以使用UIWindow來實現(設置windowLevel為Alert級別),網上有很多例子,這里就不詳細說了。
我的上一篇文章UIWindowLevel詳解,中講到了關於Windowlevel的東西,當時還只是看了看文檔,知道有這么一回兒事。今天剛好遇到這塊兒的問題,就順便仔細看了一下UIWindow方面的東西,主要包括:WindowLevel以及keyWindow兩個方面。
一、UIWindowLevel
我們都知道UIWindow有三個層級,分別是Normal,StatusBar,Alert。打印輸出他們三個這三個層級的值我們發現從左到右依次是0,1000,2000,也就是說Normal級別是最低的,StatusBar處於中等水平,Alert級別最高。而通常我們的程序的界面都是處於Normal這個級別上的,系統頂部的狀態欄應該是處於StatusBar級別,UIActionSheet和UIAlertView這些通常都是用來中斷正常流程,提醒用戶等操作,因此位於Alert級別。
上一篇文章中我也提到了一個猜想,既然三個級別的值之間相差1000,而且我們細心的話查看UIWindow的頭文件就會發現有一個實例變量_windowSublevel,那我們就可以定義很多中間級別的Window。例如可以自定義比系統UIAlertView級別低一點兒的window。於是寫了一個小demo,通過打印發現系統的UIAlertView的級別是1996,而與此同時UIActionSheet的級別是2001,這樣也驗證了subLevel的確存在。
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Alert View" message:@"Hello Wolrd, i'm AlertView!!!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:@"Cancel", nil]; [alertView show]; [alertView release]; UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"ActionSheet" delegate:nil cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Don't do that!" otherButtonTitles:@"Hello Wolrd", nil]; [actionSheet showInView:self.view]; [actionSheet release];
下面是程序運行截圖:
根據window顯示級別優先的原則,級別高的會顯示在上面,級別低的在下面,我們程序正常顯示的view位於最底層,至於具體怎樣獲取UIAlertView和UIActionSheet的level,我會在下面第二部分keyWindow中介紹並給出相應的代碼。
二、KeyWindow
什么是keyWindow,官方文檔中是這樣解釋的"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。
下面我們寫個簡單的例子看看非keyWindow能不能接受鍵盤消息和觸摸消息,程序中我們在view中添加一個UITextField,然后新建一個alert級別的window,然后通過makeKeyAndVisible讓它變成keyWindow並顯示出來。代碼如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; // Override point for customization after application launch. self.viewController = [[[SvUIWindowViewController alloc] initWithNibName:@"SvUIWindowViewController" bundle:nil] autorelease]; self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; UIWindow *window1 = [[UIWindow alloc] initWithFrame:CGRectMake(0, 80, 320, 320)]; window1.backgroundColor = [UIColor redColor]; window1.windowLevel = UIWindowLevelAlert; [window1 makeKeyAndVisible]; return YES; }
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self registerObserver]; // add a textfield UITextField *filed = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 320, 60)]; filed.placeholder = @"Input something here"; filed.clearsOnBeginEditing = YES; filed.borderStyle = UITextBorderStyleRoundedRect; [self.view addSubview:filed]; [filed release]; }
運行截圖如下:
從圖中可以看出,雖然我們自己新建了一個然后設置為keyWindow並顯示,但是點擊程序中默認window上添加的textField還是可以喚出鍵盤,而且還可以正常接受鍵盤輸入,只是鍵盤被擋住了,說明非keyWindow也是可以接受鍵盤消息,這一點和文檔上說的不太一樣。
觀察UIWindow的文檔,我們可以發現里面有四個關於window變化的通知:
UIWindowDidBecomeVisibleNotification
UIWindowDidBecomeHiddenNotification
UIWindowDidBecomeKeyNotification
UIWindowDidResignKeyNotification
這四個通知對象中的object都代表當前已顯示(隱藏),已變成keyWindow(非keyWindow)的window對象,其中的userInfo則是空的。於是我們可以注冊這個四個消息,再打印信息來觀察keyWindow的變化以及window的顯示,隱藏的變動。
代碼如下:

a、當我們打開viewDidAppear中“[self presentAlertView];”的時候,控制台輸出如下:
根據打印的信息我們可以看出流程如下:
1、程序默認的window先顯示出來
2、默認的window再變成keyWindow
3、AlertView的window顯示出來
4、默認的window變成非keyWindow
5、最終AlertView的window變成keyWindow
總體來說就是“要想當老大(keyWindow),先從小弟(非keyWindow)開始混起” 而且根據打印的信息我們同事可以知道默認的window的level是0,即normal級別;AlertView的window的level是1996,比Alert級別稍微低了一點兒。
b、當我們打開viewDidAppear中“[self presentActionSheet];”的時候,控制台輸出如下:
keyWindow的變化和window的顯示和上面的流程一樣,同時我們可以看出ActionSheet的window的level是2001。
c、接着上一步,我們點擊彈出ActionSheet的cancel的時候,控制台輸出如下:
我們看出流程如下:
1、首先ActionSheet的window變成非keyWindow
2、程序默認的window變成keyWindow
3、ActionSheet的window在隱藏掉
總體就是“想隱居幕后可以,但得先交出權利”。