詳解iOS設計中的UIWindow使用_IOS

來源:互聯網
上載者:User

每一個IOS程式都有一個UIWindow,在我們通過模板簡曆工程的時候,xcode會自動幫我們產生一個window,然後讓它變成keyWindow並顯示出來。這一切都來的那麼自然,以至於我們大部分時候都忽略了自己也是可以建立UIWindow對象。
 
  通常在我們需要自訂UIAlertView的時候(IOS 5.0以前AlertView的背景樣式等都不能換)我們可以使用UIWindow來實現(設定windowLevel為Alert層級),網上有很多例子,這裡就不詳細說了。
一、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中介紹並給出相應的代碼。

UIWindow在顯示的時候會根據UIWindowLevel進行排序的,即Level高的將排在所有Level比他低的層級的前面。下面我們來看UIWindowLevel的定義:

複製代碼 代碼如下:

const UIWindowLevel UIWindowLevelNormal;    

const UIWindowLevel UIWindowLevelAlert;    

const UIWindowLevel UIWindowLevelStatusBar; 

   

typedef CGFloat UIWindowLevel;
  IOS系統中定義了三個window層級,其中每一個層級又可以分好多子層級(從UIWindow的標頭檔中可以看到成員變數CGFloat _windowSublevel;),不過系統並沒有把則個屬性開出來。UIWindow的預設層級是UIWindowLevelNormal,我們列印輸出這三個level的值分別如下:
 

2012-03-27 22:46:08.752 UIViewSample[395:f803] Normal window level: 0.0000002012-03-27 22:46:08.754 UIViewSample[395:f803] Alert window level: 2000.0000002012-03-27 22:46:08.755 UIViewSample[395:f803] Status window level: 1000.000000 

 


  這樣印證了他們層級的高低順序從小到大為Normal < StatusBar < Alert,下面請看小的測試代碼:
 

複製代碼 代碼如下:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
self.window.backgroundColor = [UIColor yellowColor];
[self.window makeKeyAndVisible];

UIWindow *normalWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
normalWindow.backgroundColor = [UIColor blueColor];
normalWindow.windowLevel = UIWindowLevelNormal;
[normalWindow makeKeyAndVisible];

CGRect windowRect = CGRectMake(50,
50,
[[UIScreen mainScreen] bounds].size.width - 100,
[[UIScreen mainScreen] bounds].size.height - 100);
UIWindow *alertLevelWindow = [[UIWindow alloc] initWithFrame:windowRect];
alertLevelWindow.windowLevel = UIWindowLevelAlert;
alertLevelWindow.backgroundColor = [UIColor redColor];
[alertLevelWindow makeKeyAndVisible];

UIWindow *statusLevelWindow = [[UIWindow alloc] initWithFrame:CGRectMake(0, 50, 320, 20)];
statusLevelWindow.windowLevel = UIWindowLevelStatusBar;
statusLevelWindow.backgroundColor = [UIColor blackColor];
[statusLevelWindow makeKeyAndVisible];

NSLog(@"Normal window level: %f", UIWindowLevelNormal);
NSLog(@"Alert window level: %f", UIWindowLevelAlert);
NSLog(@"Status window level: %f", UIWindowLevelStatusBar);

return YES;

 
  運行結果如下圖:

我們可以注意到兩點:
 
  1)我們產生的normalWindow雖然是在第一個預設的window之後調用makeKeyAndVisible,但是仍然沒有顯示出來。這說明當Level層級相同的時候,只有第一個設定為KeyWindow的顯示出來,後面同級的再設定KeyWindow也不會顯示。
 
  2)statusLevelWindow在alertLevelWindow之後調用makeKeyAndVisible,仍然只是顯示在alertLevelWindow的下方。這說明UIWindow在顯示的時候是不管KeyWindow是誰,都是Level優先的,即Level最高的始終顯示在最前面。

二、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的顯示,隱藏的變動。
 
  代碼如下:

根據列印的資訊我們可以看出流程如下:
 
  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在隱藏掉
 
  總體就是“想隱居幕後可以,但得先交出權利”。


 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.