[iOS] UIStackView
以下是針對UIStackView的說明,排版和內容是以Stack view的官方文件為主,但不是對於原文的完全的翻譯,有加入自己的理解和詮釋,並且進行刪減,要找完全正確的翻譯就找別的文章吧。
概述
UIStackView是針對水平或是垂直排列的views進行佈局的class。
UIStackView將使Auto Layout的效能最大化的被使用。可建立出動態適配於不同的設備方位(device's orientation)、螢幕尺寸以及可取用的空間大小。
如何佈局
UIStackView會管理在它 arrangedSubviews 中的所有view的佈局。這些view會沿著stack view的軸去排列,順序是依照這些views在arrangedSubviews裡面的順序。而佈局會和Stack View的axis、distribution、alignment、space和其他的特性有關。
如何使用
打開要編輯的Storyboard,拖放一個水平或是垂直的Stack View,拉出Stack View的範圍,把想放入的view或是control拖進去。Interface Builder根據拖入的物件調整Stack View的尺寸,或是你可以在Attributes inspector裡面手動調整stack內容的外觀。
使用者要負責定義stack的位置和(選擇性的)大小。佈局和內容的大小就讓stack管理就好。
Stack佈局的邏輯
設定isLayoutMarginsRelativeArrangement
Stack會把它arranged views裡面的物件邊緣和stack自己的軸對齊。舉例來說,水平的stack會把第一個物件的leading edge和stack自己的leading edge對齊,把最後一個物件的trailing edge和stack自己的trailing edge對齊。如果isLayoutMarginsRelativeArrangement為true,會改對齊彼此的margin。
Distributions
除了fillEqually之外,stack會使用每一個arranged view的intrinsicContentSize來stack軸方位上的尺寸。fillEqually會讓所有的arranged views的大小相同去填滿stack軸方位上的空間。如果可能的話,stack會把全部的arranged views的尺寸拉伸成和沿著stack軸方位上擁有最大intrinsicContentSize值的arranged view的尺寸一樣。
Alignment
除了fill以外的參數,stack會使用每一個arranged views的intrinsicContentSize去計算他們在垂直於stack軸方位上的尺寸。fill會讓所有的arranged views的大小滿足於填滿垂直於stack軸方位上的空間。如果可能的話,stack會把全部的arranged views的尺寸拉伸成和垂直於stack軸方位上擁有最大intrinsicContentSize值的arranged view的尺寸一樣。
Stack View的位置和大小
Stack view 讓你不需要直接使用Auto layout去佈局他裡面的arranged views。但你還是要對stack view使用auto layout去定義它的位置。
也就是說你最少要固定stack view的兩個相鄰邊去定義他的位置。如果沒有額外的constraint,系統會根據stack view的內容去計算它的大小。
- 平行於stack軸的方位:會採用所有arranged views的尺寸和他們之間間隙尺寸的總和。
- 垂直於stack軸的方位:會以擁有最大尺寸的arranged view的大小為主。
- 如果isLayoutMarginsRelativeArrangement為true,stack view的尺寸會多增加margins。
你可以提供額外的constraints去定義stack view的高度、寬度。Stack會據此調整佈局和它arranged views的大小。確實的佈局會隨著stack view的properties而改變(參考UIStackViewDistribution and UIStackViewAlignment)。
BaseLine
你也可以用first baseline或last baseline來取代top、bottom或center Y。如同Stack view的fitting size一樣,baseline會由stack view的內容物來計算出來。
- 水平的stack view:會回傳最高的view給
forFirstBaselineLayout和forLastBaselineLayout方法。如果最高的view他也是stack view,那他會回傳它的forFirstBaselineLayout或forLastBaselineLayout所回傳的view。 - 垂直的stack view:會回傳他第一個arranged view給
forFirstBaselineLayout,最後一個arranged view給forLastBaselineLayout。如果這些views也是stack,那就一樣回傳該stack的forFirstBaselineLayout或forLastBaselineLayout所回傳的view。
Baseline對準只有在view的高度和他自己本身的intrinsic content size的高度一樣時才有效,如果view有拉伸或是壓縮,那baseline會在錯誤的位置。
一般的Stack view佈局
以下是Stack view使用的一般情況。
僅定義Stack view的位置
你只要固定stack view的兩個鄰邊去定義他的位置,stack view的在兩個維度上的大小就是自由的,會隨著他的arranged views而變化。特別是當你希望stack view的內容要呈現出真正的intrinsic content size的情況時,比方說label。
定義沿著Stack軸方向上的尺寸
固定stack軸方位上的兩端edge到他的superview,這就會定義出stack view在那個維度上面的大小,再定義剩餘的兩邊中的一邊,這就會定義好Stack view的位置。Stack view會調整他的內容物件在stack軸方位上的大小與位置去填滿給定的空間。然而,沒有固定的edge會基於最大的arranged view的尺寸而自由移動。
你可以給定arranged view的content hugging priority去指定內容物件彼此的壓縮順位。
定義垂直於Stack軸方向上的尺寸
類似前者,但你要固定的邊是垂直於Stack軸的兩端以及平行於Stack軸的一端。因為剩下一端平行於Stack軸沒有固定,所以stack會隨著你增減arranged view而改變本身的大小。
給定Stack的位置與尺寸
就是固定Stack的四個邊。如此的話,就會依照你提供的範圍大小去佈局arranged views。
假設有一個垂直排列的Stack,上面是一張圖片,下面是一個說明圖片的label,你需要做一些額外的定義才能讓這個Stack依照你希望的去佈局。
首先,Stack會調整label的尺寸,而非image,所以你要將image的content hugging priority調得比label的要低,這樣才會先調整label的大小。
然後為了讓image的比例正確,調整image的content mode為aspect fit,並且指定image的寬度和stack相同。
小結
基於你固定的Stack view的edge數目不同,效果會不同。
- 兩鄰邊
- 三鄰邊(兩種情況,軸的兩端再加一邊,或是垂直於軸的兩端再加一邊)
- 四鄰邊
管理Stack View的外觀
Stack是View的非渲染(nonrendering)子類,這表示他並不會提供任何的使用者介面,他是負責管理arranged views的位置與尺寸。因此諸如像是backgroundColor之類的設定在Stack view上是不管用的,你也無法覆寫Stack View的layerClass和draw之類的方法。
以下是可用來定義Stack View佈局的properties:
- axis:排列Stack View的arrangedViews的方向(軸)。
- distribution:定義arrangedViews平行於軸方向的佈局。
- alignment:定義arrangedViews垂直於軸方向的佈局。
- spacing:定義arrangedViews之間最小的距離
- isBaselineRelativeArrangement:決定views垂直方向上的spacing是否從baseline開始量(和spacing有關)。
- isLayoutMarginsRelativeArrangement:決定arrangedViews之間的佈局是不是要相對於layout margins。
在大多數的情況下你會用單一個stack view管理的對象不會太多。你可以建立巢狀的stack view的view層結構。
你也可以加上一些constraint給arrangedView,Stack view會用這些constraints去佈局。但要注意不要造成constraint conflict。
維持Subviews和Arranged Subview的一致性
Stack View有subviews和arrangedSubviews兩個屬性,它的view hierarchy裡面的一個view一定是它的subview,但不一定是它的arranged subview,這表示Stack裡面的views不一定要接受stack的佈局管理,但依舊可以是stack的view hierarchy的一部分。
- 如果從subviews內移除一個view,那一定也要從arrangedSubviews內移除它。
- 如果從arrangedSubviews內移除一個view,那不需要從subviews內移除它。
- 如果加入view到arrangedSubviews裡面,那也一定要加到subviews裡面。
所以arrangedSubviews必定是subviews的子集合。但這兩個arrays的順序是獨立的。
- arrangedSubviews裡面的次序表示views在stack view裡面排列的順序。index小的會在大的前面,以英文習慣來說,水平的stack view是左到右排列,垂直的stack view會上到下排列。
- subviews裡面的次序表示views在z軸上的次序。 index小的在index大的view下面。
動態改變Stack view的內容
當view加入、移除或是插入arrangedSubviews時,或者是arranged subviews裡面有view的hidden改變時,Stack view會自動去更新。
let view = stackView.arrangedSubviews[0]
view.isHidden = trueStack view的properties改變時,Stack view也會有反應。
對於這些更動,也可以做出動畫效果,只要你把這些更動的code放到animate裡面去。但是對於加入view到arrangedSubviews來說,這不會觸發任何動畫效果。
