在日常開發中我們經常會看到,當superview的bounds發生變化的時候,他的subview也會隨之移動,但這是為什么呢?在superview的size發生變化的時候,subview內部發生了什么呢?看似簡單的現象中其實包含了很多知識,估計再接下來很長的時間里我將仔細梳理關于Layout方面的知識,若大家在閱讀過程中發現什么問題請及時糾正,共同進步??
說起 layout 其實我們在開發過程中無時不刻不在用,特別是在寫界面的時候,比如適配不同的設備大小,橫屏豎屏轉換的時候界面不能亂,有些 app 需要適配 iphone 和 ipad 兩種設配的不同尺寸,還有tableview cell的內容自適應,有些時候當隱藏導航欄和現實導航欄的時候界面可能會伸縮或拉長等等,所以說 layout 在我們寫界面是很有用的,因此他真的很重要。
Layout 有三種主要的表現形式。分別是:
- Manual Layout(手動布局):當 superview 的大小發生變化的時候,會接受到一個
layoutSubviews
的信息, 之后你通過重寫layoutSubviews
方法來手動的重新為 subviews 布局,很明顯這將會導致很大的工作量- Autoresizing(自動調整尺寸):這種方式是 iOS6 之前的一種自動布局方式。當 superview 的大小改變時,subview 會通過
autoresizingMask
屬性來調整值。這個會在后面講到- Autolayout(自動布局):它是在 iOS6 引進的, 主要通過 view 的
constraint
來實現的。 一個 constraint 其實就是一個NSLayoutConstraint
實例對象,通過數字來描述一個 view 相對于其他view 的大小和位置。這比autoresizeingMask
更精密更容易理解,一個 view 可以有多個 constraint, 并且他們可以描述任意兩個 view 之間的關系。其實 auotolayout 在背后幫你實現了layoutSubviews
方法, 你可以不用手寫代碼來實現很多復雜的約束。你的 lauout 可以使用以上三種的任何一個或多個的組合,但一般來說我們很少會用手動布局,因為工作量大且效率較低。AutoresizeSubviews 默認會自動開啟,除非你設置 superview 的
autoresizesSubviews
屬性為 false,或者你使用了autolayout 則AutoresizeSubviews 將會被禁用。Autolayout 可以用在任何你想用的地方,是不是很方便呀。需要注意的是,在 Xcode7 及 7以后的版本中,autolayout 會在xib
或者storyboard
中默認勾選, 如果沒有勾選的話則會使用autoresizng
而不是autolayout
喲!
Autoresizing
Autoresizing 主要是通過設置 subview 的
spring
和struts
屬性來實現的。Spring 可以被拉伸,但是 strut 不可以,Spring 和 struts 可以在外部或內部,水平或垂直方向指定,因此你可以通過這兩個屬性來指定一個view要如何調整大小護著調整位置。
試想一下,一個 view 在他的 super的中心位置,當父視圖調整大小時,view也會跟著調整大小。view 將會在其外部是固定(struts)的在內部是彈性(spring)的
繼續設想有一個view在他的 superview 的中心位置,但是 view 不隨父視圖大小的變化而變化,這時view將會在外部 spring 內部 structs
再設想有一個OK按鈕在他父視圖的右下角位置,則 view 在內部是 struts,在他右方和下方是外部 struts,在上方和左方則是外部 spring。
最后設想有一個 textfield 在其 superview 的頂部,并且與 superview 同寬,這時他的外部是 struts, 但是下邊是 spring, 誰知方向時內部 struts 豎直方向內部 spring
對于上面所講到的幾種情況大家可以先畫圖理解一下。在代碼中
spring
和struts
是通過的 view 的autoresizingMask
屬性來設置的。autoresizingMask 中的 options 是位掩碼, 這些位掩碼是在定義在一個 struckt 里面,這些位掩碼表明了哪些是需要 spring 的,也就是說,沒有專門設置 struts 的選項,只要沒有設置,就是 struts 的。默認值是.None,也就意味著,所有的方向都是 struts,但是這是不可能的,當 superview 改變的時候,subview 肯定會改變,所以,.None 并不是所有都是 struts ,而是 .FlexibleRightMargin 和 .FlexibleBottomMargin 。
下面我門來舉個例子,理解一下Autoresizing
let v1 = UIView(frame: CGRectMake(100,111,132,194))
v1.backgroundColor = UIColor.magentaColor()
let v2 = UIView(frame: CGRectMake(0,0,132,10))
v2.backgroundColor = UIColor.greenColor()
let v3 = UIView(frame: CGRectMake(v1.bounds.width-20, v1.bounds.height-20,20,20))
v3.backgroundColor = UIColor.redColor()
self.view.addSubview(v1)
v1.addSubview(v2)
v1.addSubview(v3)
運行一下你將會看到下圖界面:
接下來我們為 v2 和 v3 分別添加 spring 和 struct 讓他們看起來好像一個 textfild 在界面頂部, 一個 ok 按鈕在界面的右下角
v2.autoresizingMask = .FlexibleWidth
v3.autoresizingMask = [.FlexibleTopMargin, .FlexibleLeftMargin]
v1.bounds.size.width += 40
v1.bounds.size.height -= 50
運行結果如下圖:
上面這個例子可以看得出來 autoresizing 都做了哪些事情, 不過還是有點人為的改變superview的大小,在日常開發過程中 superview的大小變化通常都是自動的,不受人為控制,比如旋轉屏幕到橫屏狀態,接下來我們通過改變v1的frame來達到想要的效果
v1.frame = self.view.bounds
v1.autoresizingMask = [.FlexibleHeight, .FlexibleWidth];
運行一下可以看到,v1充滿了整個屏幕
我們再旋轉一下屏幕可以看到
現在是橫屏狀態但 view 的布局并沒有錯亂,還是在正確的位置上
現在我們可以看出采用 autoresizing 布局還是蠻簡單的,也正是因為他太過簡單只能用來描述 superview 和 subview 之間的位置關系,無法描述view 與 view 之間的關系。在有 autolayout 之前,如果要使用 auto resizing 來實現比較復雜的布局還需要介入手動布局,調用
layoutsubviews
方法,這種方式一方面比較麻煩,另一方面還容易出錯。所以還是讓我們好啊后來研究一下 Autolayout 吧!看看它帶給我們哪些便利!