iOS UIWindow 詳解

查看滴滴開(kāi)源的 DoraemonKit 以及阿里開(kāi)源的youku-sdk-tool-woodpecker
時(shí), 看到啟動(dòng)入口均采用 UIWindow 來(lái)實(shí)現(xiàn), 效果如下圖懸浮的綠色按鈕.

懸浮按鈕

拖動(dòng)懸浮按鈕可以隨手指一動(dòng)(圖上為模擬器效果), 點(diǎn)擊懸浮按鈕可以切換頁(yè)面.

對(duì)這種效果的實(shí)現(xiàn)方式, 我的第一直覺(jué)是采用在 VC 的 touch 事件中進(jìn)行處理, 但是按鈕還需要有點(diǎn)擊效果, 這個(gè)就難以處理了. 帶著疑問(wèn)查看源碼, 發(fā)現(xiàn)兩個(gè)項(xiàng)目中的懸浮框均是采用 UIWindow 的方式來(lái)實(shí)現(xiàn), 十分巧妙. 自己關(guān)于Window 的使用方式做了一些嘗試, 遂有此文.

1 新建的 UIWindow 如何顯示?

開(kāi)始時(shí), 我首先把創(chuàng)建的 UIWindow 添加到 keyWindow 上, 調(diào)用 hidden=NO 方法顯示正常(注意window實(shí)例要被對(duì)象持有呀页,避免創(chuàng)建后就銷毀), 但是在使用 Xcode 中的渲染工具debug view hierarchy查看 App 的渲染層級(jí)時(shí), 直接崩潰. 反復(fù)查找, 移除添加到 keyWindow 的代碼, 顯示和渲染均正常.

UIWindow 屬于一個(gè)完整渲染層的容器控件, 不能再添加其他 UIWindow 實(shí)例. 一個(gè) App 應(yīng)用屬于一個(gè)最底層容器, 可以同時(shí)承載不同的 UIWindow. 多個(gè) UIWindowApp 中的顯示順序, 按照 windowLevel 的大小, 從前到后進(jìn)行顯示. App 中多個(gè) UIWindow 的響應(yīng), 和 View 中多個(gè) 子View 的響應(yīng)方式一樣, hitTest 方法返回哪個(gè)UIView, 哪個(gè)對(duì)象就響應(yīng).

iOS13 中使用多場(chǎng)景 SceneDelegate 時(shí), 在 - (void)scene: willConnectToSession: options: 方法中創(chuàng)建 Window 時(shí), 要采用 initWithWindowScene: 的方式, 否則 Window將無(wú)法顯示. 可以采用修改 Xcode 模板的方式, 在新建工程時(shí)默認(rèn)去除 SceneDelegate, 操作方式可參考:MyTemplate.

2 keyWindow 和 delegate 的 Window

一般在 application:didFinishLaunchingWithOptions: 方法中新建 Window, 作為 AppDelegate 的屬性. 然后調(diào)用 makeKeyAndVisible 方法顯示在 App 中. 此時(shí)通過(guò)[UIApplication sharedApplication].keyWindow 獲取的為此 Window. 新建 UIWindow 實(shí)例, 調(diào)用makeKeyAndVisible 后, 可以改變項(xiàng)目中的 keyWindow. 比如系統(tǒng)的 UIAlertView/UIAlertController/KeyBoard 顯示時(shí), 會(huì)自動(dòng)切換 UIApplicationkeyWindow. 將某些控件顯示在 keyWindow 時(shí), 需要注意變化, 避免控件意料之外的消失.

UIWindowhidden 屬性設(shè)置為 false 時(shí), 即可在 APP 顯示, 為何還要置位 KeyWindow 呢? 看一下文檔上 makeKeyAndVisible 的介紹:


Use this method to make the window key without changing its visibility. 
The key window receives keyboard and other non-touch related events. 
This method causes the previous key window to resign the key status.

主要在第二點(diǎn), 設(shè)置為 key window 后, 可以響應(yīng)鍵盤(pán)和非觸摸事件. App 中的事件一共包含 7 種:


typedef NS_ENUM(NSInteger, UIEventType) {
 UIEventTypeTouches,
 UIEventTypeMotion,
 UIEventTypeRemoteControl,
 UIEventTypePresses API_AVAILABLE(ios(9.0)),
 UIEventTypeScroll      API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos) = 10,
 UIEventTypeHover       API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos) = 11,
 UIEventTypeTransform   API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos) = 14,

后面 3 類為 iOS13 新增, 文檔上也沒(méi)有介紹. 日常開(kāi)發(fā)中接觸到的主要是前面 4 類. 第一類為 touch 事件, 是否為 keyWindow 均可以響應(yīng); 第二類為加速計(jì)事件, 主要指shake; 第三類為遠(yuǎn)程控制, 比如airpod 對(duì)音量的控制; 第四類為按壓事件, 文檔上解釋為a physical button, iPhone 上符合條件的只有音量鍵和電源鍵.

UIWindow 在顯示/隱藏浸船、keyWindow 切換時(shí)收捣,會(huì)發(fā)送以下 4 種通知:


UIWindowDidBecomeVisibleNotification
UIWindowDidBecomeHiddenNotification
UIWindowDidBecomeKeyNotification
UIWindowDidResignKeyNotification

監(jiān)聽(tīng)以上通知,測(cè)試鍵盤(pán)和2/3/4 類事件發(fā)生時(shí)鄙币,多 UIWindow 的響應(yīng)情況。粘貼代碼過(guò)于繁瑣,直接說(shuō)下測(cè)試結(jié)論:

  1. motion中的shake事件中徽千,keyWindow 才可以響應(yīng);
  2. 監(jiān)聽(tīng)音量鍵通知AVSystemController_SystemVolumeDidChangeNotification前汤锨,需要調(diào)用 [[UIApplication sharedApplication] beginReceivingRemoteControlEvents] 方法双抽,非 keyWindow 也可以正常監(jiān)聽(tīng), 這和文檔介紹的不同;
  3. keyWindow 中的 UITextFieldUITextView 控件響應(yīng)時(shí), 會(huì)自動(dòng)將所在的 Window 置位 keyWindow.

makeKeyWindowmakeKeyAndVisible 不同闲礼,看下 makeKeyWindow 的介紹:


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.

makeKeyWindow 會(huì)把當(dāng)前 Window 放在其他同級(jí)別/低級(jí)別的 Window 前面牍汹,但是對(duì)事件響應(yīng)沒(méi)有絲毫影響铐维。當(dāng)keyWindow 被隱藏后,系統(tǒng)會(huì)自動(dòng)把級(jí)別最高的 Window 置為 keyWindow.

3 多 Window 的使用

開(kāi)頭的例子慎菲,將懸浮框看做 UIWindow 的實(shí)例嫁蛇,可以響應(yīng) 觸摸事件。添加 panGesture手勢(shì)露该,在響應(yīng)方法中睬棚,更新 UIWindow 的位置。同時(shí)在 UIWindow 添加 UIButton 控件解幼,進(jìn)而在點(diǎn)擊方法中進(jìn)行頁(yè)面操作抑党。自己簡(jiǎn)單實(shí)現(xiàn)了一下 FloatingView

使用 UIWindow 可以仿照系統(tǒng) UIAlertView 的實(shí)現(xiàn)方式,隨心所欲的 show 到最頂層撵摆。將 VC 上添加一個(gè) UIWindow 屬性(創(chuàng)建時(shí)默認(rèn)為屏幕大小)底靠,自身作為UIWindow的根控制器,設(shè)置UIWindowhidden 進(jìn)行顯示特铝。這種方式有一個(gè)注意點(diǎn)暑中,UIWindow 對(duì) rootViewController 是強(qiáng)引用,會(huì)和 VC 造成循環(huán)引用鲫剿,在 Window 隱藏時(shí)鳄逾,主動(dòng)將 rootViewController 置為 nil, 打破循環(huán)。

使用 Window 還可以實(shí)現(xiàn)順利啟動(dòng)廣告圖牵素。

總結(jié)

可以將 App 看做一個(gè)容器严衬,里面可以裝多個(gè) UIWindow,顯示順序和 windowLevel 的大小有關(guān)笆呆。
UIWindow 間彼此獨(dú)立请琳,不能相互添加,否者渲染層級(jí)會(huì)出問(wèn)題赠幕。非keyWindowkeyWindow 間對(duì)系統(tǒng)事件的響應(yīng)方式不同俄精,通過(guò) makeKeyWindow 可以調(diào)整多 Window 的顯示,通過(guò) makeKeyAndVisible 可以切換 keyWindow. 通過(guò)多 UIWindow榕堰,可以很方便的模仿 UIAlertView 的實(shí)現(xiàn)竖慧,實(shí)現(xiàn)自定義彈窗以及啟動(dòng)圖。
Demo

喜歡和關(guān)注都是對(duì)我的鼓勵(lì)和支持~
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末逆屡,一起剝皮案震驚了整個(gè)濱河市圾旨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌魏蔗,老刑警劉巖砍的,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異莺治,居然都是意外死亡廓鞠,警方通過(guò)查閱死者的電腦和手機(jī)帚稠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)床佳,“玉大人滋早,你說(shuō)我怎么就攤上這事∑雒牵” “怎么了杆麸?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)浪感。 經(jīng)常有香客問(wèn)我角溃,道長(zhǎng),這世上最難降的妖魔是什么篮撑? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮匆瓜,結(jié)果婚禮上赢笨,老公的妹妹穿的比我還像新娘。我一直安慰自己驮吱,他們只是感情好茧妒,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著左冬,像睡著了一般桐筏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拇砰,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天梅忌,我揣著相機(jī)與錄音,去河邊找鬼除破。 笑死牧氮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瑰枫。 我是一名探鬼主播踱葛,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼光坝!你這毒婦竟也來(lái)了尸诽?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤盯另,失蹤者是張志新(化名)和其女友劉穎性含,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體土铺,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胶滋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年板鬓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片究恤。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡俭令,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出部宿,到底是詐尸還是另有隱情抄腔,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布理张,位于F島的核電站赫蛇,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏雾叭。R本人自食惡果不足惜悟耘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望织狐。 院中可真熱鬧暂幼,春花似錦、人聲如沸移迫。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)厨埋。三九已至邪媳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荡陷,已是汗流浹背雨效。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留废赞,地道東北人设易。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蛹头,于是被迫代替她去往敵國(guó)和親顿肺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345