View簡介
一個view可以從nib生成,也可以在代碼中創建。View hierarchy 是主要的view組織形式。一個view可以有多個subviews,但是一個 subview只能有一個直接的superview。所以很多view就會組成一棵樹。如果一個view被移出view hierarchy,它的子類也會被移除;如果一個view被隱藏,它的子類也會被隱藏;如果一個view移動,它的子類也會被移動。
The Window
View hierarchy的頂層是應用的window,是UIWindow(UIView 的子類) 的一個實例。你的應用應該只有一個main window。它將在應用啟動時被創建并且不會被銷毀或者代替。它是應用的背景并且是終極superview,也就是所有其他的view都是它的subviews。
假如你的應用要顯示在外接屏幕上,就需要創建額外的UIWindow
應用的window必須填充設備的screen,具體的做法是在window初始化時把window的frame設置成screen的bounds。使用main storyboard的話這個事情會由UIApplicationMain函數在應用啟動的時候自動完成。如果不用main storyboard的話就需要自己在應用的聲明周期中創建window并且設置好frame.
這個window必須在應用而生命周期中一直保持著。為了做到這樣,app delegate類會用一個strong retain policy來持有一個 window屬性。具體的過程是:在應用啟動時,UIApplicationMain 方法會初始化app delegate類并且一直持有它,然后window實例就會被賦值到app delegate的 window屬性上,所以也會被一直持有。
通常來說,你會得到一個view controller并且會被賦值到main window的rootViewController屬性上。如果你用的是main storyboard,這都都會自動初始化好。當一個view controller 成為main window的rootViewController,它的view就成為了 main window有且僅有的一個直接subview,也就是main window 的root view。之后所有的view 都只能是這個root view的 subview。也就是說root view是view hierarchy中用戶通常能看到的地位最高的對象。
但是有些時候用戶可能會看到root view之后的window,所以最好給這個window設置合適的backgroundColor。但通常來說我們沒有理由去對window本身做任何修改。應用的界面在對應的window被設置為key window之前都是不可見的。這個可以通過調用UIWindow實例的makeKeyAndVisible方法來完成。
main window從創建、配置到顯示的過程:
- 使用main storyboard
- storyboard文件在Info.plist的鍵為Main storyboard file base name中指定(UIMainStoryboardFile)
- UIapplicationMain實例化UIWindow并設置好frame
把設置好的UIWindow的實例指定給app delegate的window屬性 - 實例化view controller并指定給window的 rootViewController屬性
View Hierarchy的特點
- 如果一個view被移出或者引入它的superview,它的subview會跟著被移出或引入;
- 一個view的透明度會被其subview繼承;
- 一個view可以限制subview的顯示范圍,比如不讓subview超出 view本身的范圍,這叫做clipping,被設置在clipsToBounds 屬性中;
- 一個superview擁有它的subview;
- 如果一個view的尺寸變化了,它的subview也會自動被重新設置尺寸。
一個UIView有一個superview屬性和一個subviews屬性(一個 UIView對象的數組,back-to-front順序)。另外也有一個 isDescendantOfView: 方法來檢查一個view是不是另一個view 的subview。View還有一個tag屬性,可以通過viewWithTag: 來進行引用。
在代碼中操作view hierarchy很簡單。addSubview: 方法添加一個subview,removeFromSuperview移除一個subview。
當addSubview:被調用時,這個view會被放到其superview的 subview數組中的最后一個,也就是說會被最后畫出來,即出現在最前面。一個view的subviews是被索引的,從0開始(rearmost)。可以把一個view插入到指定位置,以及放到前面/后面,或交互兩個view。
- insertSubview:atIndex:
- insertSubview:belowSubview:,
- insertSubview:aboveSubview:
- exchangeSubviewAtIndex:withSubviewAtIndex:
- bringSubviewToFront:, sendSubviewToBack:
奇怪的是,沒有一個方法可以直接移除一個view的所有subview。然而,因為一個view的subview數組是一個不可變的數組,所以可以用如下方法一次移除全部:
myView.subviews.forEach{$0.removeFromSuperview}
Visibility and Opacity
視圖的可見性可以通過設置hidden屬性來更改。一個隱藏的view無法接收觸摸事件,所以對于用戶來說相當于不存在,但實際上是存在的,所以仍然可以在代碼中對其操作。
View的背景顏色可以通過其backgroundColor屬性來設置,顏色屬于UIColor類。如果backgroundColor為nil那么背景就是透明的??梢酝ㄟ^設置view的alpha屬性來修改透明程度,1.0是完全不透明,0.0是透明。假設一個view的alpha是0.5,那么它的subview 的alpha都是以0.5為基準的,不可能高于0.5。而 UIColor也有 alpha這個屬性,所以即使一個view的alpha是 1.0,它仍舊可能是透明的,因為其 backgroundColor可以是透明的。一個alpha為 0.0 的 view是完全透明的所以是不可見的,通常來說也不可能被點擊。
View的alpha屬性不僅影響背景顏色,也會影響其內容的透明度。
View的opaque屬性的修改并不會影響view的樣子,更多的是對于系統繪制時的提示。如果一個view的opaque設為true,因為不用考慮透明的繪制,所以效率會高一點,并且再設置透明的背景顏色或者 alpha屬性都無效??赡軙屓顺泽@,它的默認值是true。
Frame
View的frame屬性(CGRect 類)是它本身的長方形在superview 中的位置,注意是在superview的坐標系中的位置。默認來說,superview的坐標系原點在左上,向右x增加,向下y增加。
Bounds&Center
當創建一個UIView時,其bounds的坐標原點是(0.0, 0.0),也就是左上角,如果改變了bounds的原點,也就改變了其坐標系,其 subview一般也會有變化,下面代碼描述了這種情況
Trait Collections and Size Classes
界面上的每個view都有一個traitCollection屬性,值是一個 UITraitCollection,包含下面四個屬性:
- displayScale,由當前屏幕決定的縮放尺寸,1(single resolution) 2(double resolution) 3(iPhone 6/6s Plus)
- userInterfaceIdiom,一個UserIterfaceIdiom值,可能是 .Phone 或 .Pad,來標志不同的設備,默認來說和UIDevice 的userInterfaceIdiom屬性一致
- horizontalSizeClass, verticalSizeClass,是 UIUserInterfaceSizeClass值,可能是.Regular 或.Compact
- 水平和豎直都是.Regular -> iPad
- 水平是.Compact,豎直是.Regular->iPhone在垂直方向,或者 iPad 的分屏應用
- 水平和豎直都是 .Compact -> iPhone 在水平方向(iPhone 6/6s plus除外)
- 水平是 .Regular 豎直是 .Compact -> iPhone 6/6s Plus 在水平方向
當應用運行時如果 trait collection 發生改變,會調用 traitCollectionDidChange 方法