1. 概述
本人最近在寫一個自定義彈框(去看看), 在不停的跑demo過程中, 發現怎么也無法將彈窗顯示出來, 然后打斷點進行調試, 發現[UIApplication sharedApplication].keyWindow竟然為nil:
然后各種找原因, 大概原因就是在用[UIApplication sharedApplication].keyWindow獲取keywindow的時候, keywindow并沒有被創建, 需要在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
中設置window并makeKeyAndVisible
; 我按照這個來做了, 還是不行!!!!
于是繼續找, 有的說是iOS7.0會有[UIApplication sharedApplication].keyWindow
獲取不到keywindow的情況, iOS8.0就好多了, 這種情況下可以用[[[UIApplication sharedApplication] delegate] window];
代替, 然后試著這樣做了, 結果還是現實不出來, 點擊調試視圖看到我要的彈窗竟然被window蓋住了, 有圖有真相:
2. 原因分析
前面看了網上一些分析, 其實有時候并不是十分適合自己的情況, 但也能提供很多思路的指導, 本人自己的分析原因大概是這樣的:
最主要的原因是本人將創建和彈出彈窗的代碼放在了viewWillAppear
方法里面, 為什么這樣是不行的呢, 咱們具體代碼具體分析, 首先先看AppDelegate.m
里面的代碼是這樣的
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
RSViewController *VC = [[RSViewController alloc] init];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = VC;
[self.window makeKeyAndVisible];
return YES;
}
這里已經設置了makeKeyAndVisible
, 所以說上面說的對應的問題應該是不存在了;
接下來看看創建彈窗的代碼, 是在RSViewController.m文件里的:
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
/*
登錄的彈框
d*/
RSAlertView *alerV = [[RSAlertView alloc] initIfLogin];
alerV.registerBlock = ^{
NSLog(@"注冊的block");
};
alerV.loginBlock = ^(){
NSLog(@"登錄的block");
};
[alerV showRSAlertView];
}
看到這里還是看不出問題是吧, 那我們可以試著驗證一下是不是"通過[UIApplication sharedApplication].keyWindow
獲取keywindow的時候沒有創建keywindow并makeKeyAndVisible", 為了便于標注, 我就上截圖吧, 在箭頭的地方分別加上幾個BSLog
還有這里
然后將項目跑起來, 打印出來的結果是這樣的:
注意看順序!!!注意看順序!!!注意看順序!!!注意看順序!!!注意看順序!!!注意看順序!!!注意看順序!!!注意看順序!!!注意看順序!!!
果然是剛剛說的原因, 也就是系統在調用 [UIApplication sharedApplication].keyWindow
之后才makeKeyAndVisible
!!!!!!
知道原因在哪了吧!!!!!!
所以為了能繼續順利測試, 我們必須要在makeKeyAndVisible
之后才使用[UIApplication sharedApplication].keyWindow
, 要正確的判斷之前還是之后, 就涉及到老生常談的生命周期了, 這個這里就不贅述了, 要不然又是講半天, 這里就說怎么弄吧:
很簡單, 創建彈框的方法不要放在viewWillAppear也不要放在viewWillAppear
或者- (void)viewDidLayoutSubviews
方法里, 可以放在
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
這樣你就可以方便的進行測試了.
注意 :到了這里, 其實使用[UIApplication sharedApplication].keyWindow
或者[[[UIApplication sharedApplication] delegate] window]
都是可以實現效果的; 但還是比較推薦后者, 因為使用前者的話, 當你的app需要跳轉到別的app然后返回本app的時候, 有可能會導致UI錯亂, 使用后者就不會.
3. 疑惑
雖然上面說了不放在生命周期的方法里面, 但小伙伴們肯定會馬上機智的想到一個問題"那我使用的時候難道keywindow就能保證不是nil嗎?", 這問題問得好.
但其實大家不必擔心, 因為一般項目里面, 彈窗的出現都是需要觸發的, 例如服務器返回錯誤, 或者點擊某個按鈕的時候才會觸發, 這時候是不會出現上面的問題的, 所以小伙伴們可以放心使用.
另外, 我們也會想[UIApplication sharedApplication].keyWindow
獲取到的keywindow跟[[[UIApplication sharedApplication] delegate] window]
獲取到的有什么不同嗎?
這里大家可以看看另外的作者的分析
stackoverflow上的