本篇博文純粹是UIWindow引發(fā)的一場(chǎng)血案. 起因是在swift項(xiàng)目里添加一個(gè)廣告頁(yè)時(shí),思路是創(chuàng)建一個(gè)UIView,然后展示廣告圖片,然后將該view放在主window上,這樣無(wú)論啟動(dòng)或者從后臺(tái)回到前臺(tái)時(shí)都可以顯示廣告,并且這樣與項(xiàng)目沒(méi)有任何耦合.
那么問(wèn)題來(lái)了,在oc項(xiàng)目中,這樣做沒(méi)有任何問(wèn)題,但是在swift項(xiàng)目里,啟動(dòng)時(shí)沒(méi)有顯示,后臺(tái)回到前臺(tái)正常顯示,然后查看了啟動(dòng)時(shí)的視圖層級(jí),發(fā)現(xiàn)廣告頁(yè)在最下面.然后慘案來(lái)了.
- 1.為什么在swift中 后來(lái)加載的廣告圖片在試圖層級(jí)的最下面.
- 2.視圖層級(jí)與響應(yīng)者鏈?zhǔn)绞裁搓P(guān)系.
- 3.UISCreen UIView UIWindow UILayer他們之間的關(guān)系到底是怎么樣?
二 分析解決第一個(gè)問(wèn)題
UIWindow對(duì)象提供了應(yīng)用程序用戶界面的背景,并提供了重要的事件處理行為。 Windows沒(méi)有自己的視覺(jué)外觀,但它們對(duì)應(yīng)用程序視圖的呈現(xiàn)至關(guān)重要。屏幕上顯示的每個(gè)視圖都由窗口包圍,每個(gè)窗口與應(yīng)用程序中的其他窗口無(wú)關(guān)。您的應(yīng)用程序收到的事件最初會(huì)路由到相應(yīng)的窗口對(duì)象,然后將這些事件轉(zhuǎn)發(fā)到相應(yīng)的視圖。 Windows可以與您的視圖控制器一起實(shí)現(xiàn)方向更改,并執(zhí)行許多其他任務(wù),這些是您的應(yīng)用程序操作的基礎(chǔ)。
我們了解到:
- UIWindow是無(wú)法設(shè)置其背景色等視覺(jué)層面的屬性.
- 每一個(gè)UIWindow之間是獨(dú)立的.
我們知道每一個(gè)APP至少得有一個(gè) 主Window,其作為App的最外層,在視圖層級(jí)上,其在最上面.
我們?cè)诔跏蓟粋€(gè)App時(shí),函數(shù)會(huì)默認(rèn)創(chuàng)建一個(gè) 主window,所以如果你通過(guò)storyboard啟動(dòng)時(shí),會(huì)發(fā)現(xiàn)已經(jīng)有了window.
但是如果我們使用純代碼編程,我們需要把storyboard里面的東西給刪掉,則我們需要對(duì)默認(rèn)生成的window配置尺寸等信息,并設(shè)置其根視圖,以及最后 makeKeyAndVisible
self.window = [[UIWindow alloc]initWithFrame:CGRectMake(0, 0,[UIScreen mainScreen].bounds.size.width,[UIScreen mainScreen].bounds.size.height)];
FirstViewController* vc = [[FirstViewController alloc]init];
vc.view.backgroundColor = [UIColor yellowColor];
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];
當(dāng)然,我們可以不刪除storyboard里面默認(rèn)的視圖,那么在項(xiàng)目運(yùn)行后,我們打印輸出
[[UIApplication sharedApplication]windows]
結(jié)果為:
<UIWindow: 0x7f8915604550; frame = (0 0; 375 667); hidden = YES; gestureRecognizers = <NSArray: 0x600000242130>; layer = <UIWindowLayer: 0x60000003c5e0>>
<UIWindow: 0x7f8915604550; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x600000242130>; layer = <UIWindowLayer: 0x60000003c5e0>>
顯然,我們不需要第一個(gè)隱藏的window,這也是我在 swift項(xiàng)目中 廣告顯示頁(yè)面顯示在最下面.我查看了我獲取主Window的方法,
UIApplication.shared.windows.first!
很顯然廣告頁(yè)加載在 第一個(gè)隱藏的Window上了,而其不是APP的主Window.我們能看到的永遠(yuǎn)是主Window.
當(dāng)我們自定義創(chuàng)建一個(gè)window時(shí),
UIWindow *twoWindow = [[UIWindow alloc]initWithFrame:CGRectMake(0, 0, kScreenWidth , kScreenHeight)];
twoWindow.windowLevel = 200;
// [twoWindow becomeKeyWindow];
// [twoWindow makeKeyWindow];
twoWindow.hidden = NO;
[[UIApplication sharedApplication].delegate.window addSubview:twoWindow];
我發(fā)現(xiàn)如果不講twoWindow添加到主window上的話,我們始終看不到這個(gè)window,即使 becomeKeyWindow 或者 resignKeyWindow becomeKeyWindow,但是 po 出來(lái)是存在的.
但是,在我看來(lái) 其一旦被加載在主window上,則跟view沒(méi)啥區(qū)別了,所以不如使用view代替即可.之前有看到說(shuō)是給自定義window設(shè)置 windowlevel可以實(shí)現(xiàn)alert的自定義.目前看來(lái)該方法已經(jīng)失效,或者我的思路不對(duì).
小結(jié):
- 在初始化一個(gè)項(xiàng)目時(shí),若是純代碼編程,則最好將storyboard里面的任何東西全部刪除.這樣就不會(huì)生成一個(gè)默認(rèn)的window.
- 在創(chuàng)建window時(shí),必須為其指定一個(gè)根視圖,否則會(huì)crash.
- 一般不要?jiǎng)?chuàng)建多個(gè)window.
三回顧一下響應(yīng)者鏈
具體信息可以參考 iOS-UIResponder官方API大總結(jié)
四 UISCreen UIView UIWindow CALayer
先看下他們之間的繼承關(guān)系,如下圖所示
- UIScreen是手機(jī)用來(lái)展示APP內(nèi)容的窗口.其是物理層面上的窗口.所以一般我們用其來(lái)獲取屏幕的寬和高.
- UIView:用來(lái)展示內(nèi)容,并且可以響應(yīng)事件,其繼承于UIResponder,當(dāng)然能夠響應(yīng)事件.我們開(kāi)發(fā)中的大部分控件均繼承自UIView,不包括UIViewontroller.
- CALayer:每一個(gè)UIView都會(huì)包含一個(gè)根CALayer,UIView負(fù)責(zé)內(nèi)容的展示與響應(yīng)事件,而后者只負(fù)責(zé)內(nèi)容的繪制. 包括一些動(dòng)畫(huà).
CALayer參考博文 - UIWindow是UIView的特殊子類.