iOS UIWindow 第二彈

近期被一個(gè) UIWindow 的問(wèn)題坑慘了 ??碳褒,網(wǎng)上查了很久,沒(méi)什么資料寂恬,所以仔細(xì)再次深入研究了一下。

本文以問(wèn)題的形式闡述莱没,以下結(jié)論全部是看官方文檔以及自己試驗(yàn)得出初肉,如有錯(cuò)誤,還望指出饰躲。

UIApplication ★ keyWindow

The app's key window.

This property holds the UIWindow object in the windows array that is most recently sent the makeKeyAndVisible message.

keyWindow 的作用牙咏?

接收鍵盤事件和其他非觸摸性事件。同一時(shí)間只有一個(gè) keyWindow嘹裂。

keyWindow 指的是哪一個(gè) window妄壶?

最后一個(gè)調(diào)用 makeyAndVisiblemakeyWindow 的 window

keyWindow 的 isHidden 屬性可能為 YES。如果一個(gè) window 初始化之后寄狼,沒(méi)有調(diào)用 makeyAndVisible丁寄,直接調(diào)用 makeyWindow,就會(huì)出現(xiàn)這種情況

當(dāng)前 keyWindow 被設(shè)置 hidden = YES泊愧,系統(tǒng)默認(rèn)的下一個(gè) keyWindow 是哪一個(gè)伊磺?

我們銷毀一個(gè) window 的時(shí)候,往往這樣寫

self.window.hidden = YES;
self.window = nil;

如果這個(gè) window 恰好是當(dāng)前 keyWindow删咱,調(diào)用 hidden = YES 的時(shí)候屑埋,系統(tǒng)將默認(rèn)取消其 keyWindow,并在 hidden = YES 方法內(nèi)部設(shè)置一個(gè)新的 keyWindow

至于系統(tǒng)怎么設(shè)置新的 keyWindow

這是一個(gè)問(wèn)題 ?? ??

經(jīng)過(guò)多番測(cè)試痰滋,找到如下規(guī)律:

  • 一定是 windows 數(shù)組中摘能,用戶創(chuàng)建的 level 最大的,但是 level 最大的可能有幾個(gè)敲街,因?yàn)?level 可以相同
  • level 相同的時(shí)候团搞,一定是實(shí)際顯示在最上面的那個(gè)

總結(jié)一下就是,如果當(dāng)前的 keyWindow 設(shè)置 hidden = YES多艇,系統(tǒng)會(huì)去找 windows 數(shù)組中 實(shí)際顯示在最上層的那個(gè) window
(當(dāng)前的 keyWindow 可能就是實(shí)際顯示在最上層的那個(gè)逻恐,系統(tǒng)會(huì)將其跳過(guò))

需要注意的是,實(shí)際顯示在最上面的不一定是 windows 最后一個(gè)元素,后面會(huì)講~

keyWindow 可能為空嗎梢莽? 2019.05.06 更新

目前知道以下情況,keyWindow 可能為空奸披。

在使用 Main.storyboard 自動(dòng)初始化根控制器昏名,不手動(dòng)去調(diào)用 AppDelegate 的 window 的 makeKeyAndVisible 或者 makeKeyWindow 的時(shí)候,是可能為空的阵面。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

這個(gè)時(shí)候在上面這個(gè)方法沒(méi)有走完的時(shí)候轻局,可以打印一下,keyWindow 是為 nil 的样刷。只有等該方法走完之后仑扑,AppDelegate 的 window 才會(huì)變成 keyWindow。

需要注意的是置鼻,在 AppDelegate 的 window 還沒(méi)變成 keyWindow 之前镇饮,初始化其他 window,可能會(huì)造成 AppDelegate 的 window 顯示不正常箕母。

所以盡量不在 didFinishLaunchingWithOptions 方法中初始化其他 window储藐,實(shí)在需要的話,可以在初始化其他 window 之前嘶是,先調(diào)用 AppDelegate 的 window 的 makeKeyAndVisible 方法钙勃。

UIApplication ★ windows

The app's visible and hidden windows.

This property contains the UIWindow objects currently associated with the app. This list does not include windows created and managed by the system, such as the window used to display the status bar.

The windows in the array are ordered from back to front by window level; thus, the last window in the array is on top of all other app windows.

window 什么時(shí)候會(huì)加入到這個(gè)數(shù)組?

系統(tǒng)創(chuàng)建創(chuàng)建的 window(例如 status bar 的 window)不會(huì)被加到該數(shù)組

其他情況下聂喇,只要一個(gè) window 被初始化了辖源,就會(huì)自動(dòng)加到 windows 數(shù)組

2019.05.06 更新

其實(shí)用加入這個(gè)詞語(yǔ)不太準(zhǔn)確,經(jīng)過(guò)后期的一些了解希太,這個(gè) windows 數(shù)組應(yīng)該是一個(gè)計(jì)算屬性克饶,也就是說(shuō),在調(diào)用到該方法的時(shí)候誊辉,才去判斷哪些 window 應(yīng)該放到數(shù)組彤路,返回回來(lái)。

如果實(shí)在不想自己的 window 加入到這個(gè)數(shù)組芥映,可以繼承自 UIWindow洲尊,然后重寫下面這個(gè)私有方法(風(fēng)險(xiǎn)未知)

- (bool)isInternalWindow {
    return YES;
}

windows 數(shù)組元素順序?

從整體上來(lái)看奈偏,windows 是根據(jù) window level 逆序排列的

但是如果 level 相同坞嘀,就是后初始化的排在后面

如何改變 windows 元素順序?

調(diào)用 makeKeyWindow 或者 makeKeyAndVisible 都不會(huì)改變 windows 數(shù)組的順序惊来,唯一一個(gè)改變的方法丽涩,就是改變一個(gè) window 的 level

level 改變之后,當(dāng)然也是按照 level 逆序排序

level 相同的,后初始化的排在后面(也許是通過(guò)比較內(nèi)存地址來(lái)判斷初始化順序 ??)

顯示在最上層的 window

怎么成為最上面的 window矢渊?

頂部的 window 肯定是 level 最高的 window继准,所以想成為最上面的,直接將 level 設(shè)置的很大即可

如果出現(xiàn) level 相同的矮男,情況比較復(fù)雜

  1. 如果一個(gè) window 在其他 window 調(diào)用完各種方法之后移必,調(diào)用 makeyAndVisible 方法的可以在最上方,不管前面調(diào)用了什么方法毡鉴,反正只要在最后調(diào)用 makeyAndVisible崔泵,都可以提升到同等級(jí)的最上面
  2. 如果一個(gè) window 從來(lái)沒(méi)有調(diào)用過(guò) makeyAndVisiblehidden = NO,那么可以通過(guò)調(diào)用 hidden = NO 這句代碼猪瞬,可以達(dá)到情況 1 同樣的效果

最上面的 window 是不是 windows 數(shù)組中最后一個(gè)元素?

大多數(shù)情況下是憎瘸,但是有 level 相同的 window 的時(shí)候可能就不是了。

因?yàn)?window 無(wú)論怎么調(diào)用 makeyAndVisible 或者 makeKeyWindow 都是不會(huì)影響到一個(gè) window 在 windows 數(shù)組中的位置陈瘦,唯有改變 level 才可以幌甘。

當(dāng) level 確定之后,windows 數(shù)組中的元素順序肯定就不會(huì)變了

但是調(diào)用 makeyAndVisible 方法是可以改變同等級(jí)的 window 的上下關(guān)系的痊项,所以最上面的 window 不一定就是 windows 數(shù)組中最后一個(gè) window

說(shuō)極端一點(diǎn)的話含潘,甚至可能出現(xiàn)這樣的情況,一個(gè) window 既在 windows 數(shù)組的最后一個(gè)线婚,并且是 keyWindow遏弱,但是它實(shí)際上并不是顯示在最上面的那一個(gè) window

<span id="a">如何取到真正的顯示在最上面的 window?</span>

這是一個(gè)問(wèn)題 ?? ??

我只能說(shuō)塞弊,如果我們忽略掉 level 相同的那種特殊情況漱逸,那我們大多數(shù)情況可以這么找,基本不會(huì)出問(wèn)題

- (UIWindow *)topWindow
{
    UIWindow *topWindow = nil;
    NSArray <UIWindow *>*windows = [UIApplication sharedApplication].windows;
    for (UIWindow *window in windows.reverseObjectEnumerator) {
        if (window.hidden == YES || window.opaque == NO) {
            // 隱藏的 或者 透明的 跳過(guò)
            continue;
        }
        if (CGRectEqualToRect(window.bounds, [UIScreen mainScreen].bounds) == NO) {
            // 不是全屏的 跳過(guò)
            continue;
        }
        topWindow = window;
        break;
    }
    return topWindow?:[UIApplication sharedApplication].delegate.window;
}

幾個(gè)方法

makeKeyAndVisible 和 makeKeyWindow 的區(qū)別游沿?

makeKeyAndVisible 會(huì)改變顯示效果饰抒,并設(shè)置 keyWindow。其內(nèi)部調(diào)用了 makeKeyWindow诀黍,并且設(shè)置了 hidden = NO袋坑,除此之外應(yīng)該還有一些其他操作~

makeKeyWindow 只會(huì)設(shè)置 keyWindow

創(chuàng)建了一個(gè) window,需要顯示眯勾,一定要調(diào)用 makeKeyAndVisible 嗎枣宫?

不一定,其實(shí)直接調(diào)用 hidden = NO吃环,也可以顯示的也颤。

官方是這樣說(shuō)的:

This is a convenience method to show the current window and position it in front of all other windows at the same level or lower. If you only want to show the window, change its hidden property to NO.

大概意思就是說(shuō),makeKeyAndVisible 方法就是為了方便郁轻,大多數(shù) app 調(diào)用這個(gè)方法來(lái)顯示 main window 并且 設(shè)置為 keyWindow翅娶,并且將其放在 level 小于等于它的 window 之上文留。如果只是想要顯示一個(gè) window,直接修改它的 hidden 屬性竭沫,設(shè)置為 NO 就可以了燥翅。

那么其實(shí),我們?nèi)绻胱屢粋€(gè) window 顯示蜕提,并且不想讓它成為 keyWindow 的話不皆,就直接設(shè)置

self.window.hidden = NO;

這樣是最好的

makeKeyWindow 什么時(shí)候會(huì)被調(diào)用最住?

makeKeyWindow 方法的作用就是將一個(gè) window 設(shè)置為 keyWindow

  1. 調(diào)用 makeKeyAndVisible 方法內(nèi)部會(huì)調(diào)用 makeKeyWindow 方法
  2. 設(shè)置 keyWindow 的 hidden = YES 的時(shí)候癣缅,系統(tǒng)會(huì)找一個(gè) window蔗牡,設(shè)置為 keyWindow霎俩,所以也會(huì)調(diào)用 makeKeyWindow

window 的 setHidden: 方法

一個(gè) window 被創(chuàng)建的時(shí)候危虱,其 hidden 屬性默認(rèn)是為 YES 的拌消。

當(dāng)設(shè)置 hidden = NO 即可顯示一個(gè) window置蜀,無(wú)需將 window 添加到一個(gè) superview 上面镣煮。

當(dāng)設(shè)置 hidden = YES 會(huì)將這個(gè) window 隱藏姐霍。如果這個(gè) window 是 keyWindow,那么這個(gè) setHidden: 的方法內(nèi)部會(huì)取消當(dāng)前 window 是 keyWindow 的設(shè)置典唇,并且會(huì)找一個(gè)新的 window 來(lái)設(shè)置為 keyWindow

使用時(shí)的注意點(diǎn)

因?yàn)閼?yīng)用內(nèi)長(zhǎng)期濫用 window镊折,很多彈窗浮層都是 window,并且有些第三方的 SDK 里面也搞了一些奇奇怪怪的 window介衔。導(dǎo)致很多時(shí)候在獲取 window 會(huì)出現(xiàn)一些亂七八糟的問(wèn)題恨胚,目前也沒(méi)有找到很好的方案。

所以炎咖,個(gè)人認(rèn)為赃泡,如果不是必須使用 window,就最好不要使用 window乘盼,因?yàn)?window 多了之后升熊,很不方便管理〕裾ぃ可以使 view 或者 viewController 代替~

實(shí)在要用的時(shí)候级野,注意以下問(wèn)題

添加一個(gè)視圖到最上層的 window

不要使用

  • [[UIApplication sharedApplication] keyWindow]
  • [[[UIApplication sharedApplication] windows] lastObject]

這兩個(gè)都不一定是最上層的 window,而且很有可能有問(wèn)題粹胯,最終發(fā)現(xiàn)自己的視圖不知道添加到哪里去了蓖柔,所以最好這樣找

參考前面 如何取到真正的顯示在最上面的 window?

這個(gè)方法不一定對(duì)风纠,但是相對(duì)大部分情況下都是適用的渊抽,目前沒(méi)有找到更完美的解決方法

如果你只是想顯示一個(gè) window,不想它變成 keyWindow

不要使用

[self.window makeKeyAndVisible];

直接設(shè)置 hidden = NO议忽,即

self.window.hidden = NO;

這樣可以將它顯示出來(lái)懒闷,并且不會(huì)設(shè)置為 keyWindow。如果沒(méi)顯示正常,檢查一下 window level 是否正確愤估。

如果想讓一個(gè) window 永遠(yuǎn)不要變成 keyWindow

最好是重寫 becomeKeyWindow 方法帮辟,調(diào)用 [super becomeKeyWindow] 之后,找到實(shí)際顯示在最上層的全屏 window玩焰,將其設(shè)置為 keyWindow

之前已經(jīng)說(shuō)過(guò)由驹,找實(shí)際顯示在最上層的 window 其實(shí)是有一定問(wèn)題的(level 相同的情況),將就用吧 ?? 昔园,實(shí)在沒(méi)找到更好的方法蔓榄,如果你有有更好的辦法的話,請(qǐng)告訴我~

2019.05.06 更新

經(jīng)常一番研究默刚,總算找到了讓一個(gè) window 不變成 keyWindow 的方法甥郑,可以繼承 UIWindow,然后重寫以下私有方法(風(fēng)險(xiǎn)未知)

- (bool)_canBecomeKeyWindow {
    return NO;
}

如何讓 window 不影響狀態(tài)欄 2019.05.06 更新

經(jīng)測(cè)試荤西,顯示新的 window 有時(shí)候會(huì)影響到狀態(tài)欄的展示澜搅,為了徹底避免影響到狀態(tài)欄,可以繼承 UIWindow邪锌,然后重寫以下私有方法(風(fēng)險(xiǎn)未知)

- (bool)_canAffectStatusBarAppearance {
    return NO;
}

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末勉躺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子觅丰,更是在濱河造成了極大的恐慌饵溅,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妇萄,死亡現(xiàn)場(chǎng)離奇詭異概说,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)嚣伐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門糖赔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人轩端,你說(shuō)我怎么就攤上這事放典。” “怎么了基茵?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵奋构,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我拱层,道長(zhǎng)弥臼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任根灯,我火速辦了婚禮径缅,結(jié)果婚禮上掺栅,老公的妹妹穿的比我還像新娘。我一直安慰自己纳猪,他們只是感情好氧卧,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著氏堤,像睡著了一般沙绝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鼠锈,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天闪檬,我揣著相機(jī)與錄音,去河邊找鬼购笆。 笑死粗悯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的由桌。 我是一名探鬼主播为黎,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼邮丰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼行您!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起剪廉,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤娃循,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后斗蒋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捌斧,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年泉沾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了捞蚂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡跷究,死狀恐怖姓迅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情俊马,我是刑警寧澤丁存,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站柴我,受9級(jí)特大地震影響解寝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜艘儒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一聋伦、第九天 我趴在偏房一處隱蔽的房頂上張望夫偶。 院中可真熱鬧,春花似錦嘉抓、人聲如沸索守。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)卵佛。三九已至,卻和暖如春敞斋,著一層夾襖步出監(jiān)牢的瞬間截汪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工植捎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留衙解,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓焰枢,卻偏偏與公主長(zhǎng)得像蚓峦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子济锄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容