[iOS] UIStackView

SC Tuan
SC Tuan
Jul 10, 2017 · 10 min read

以下是針對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給forFirstBaselineLayoutforLastBaselineLayout方法。如果最高的view他也是stack view,那他會回傳它的forFirstBaselineLayoutforLastBaselineLayout 所回傳的view。
  • 垂直的stack view:會回傳他第一個arranged view給 forFirstBaselineLayout ,最後一個arranged view給forLastBaselineLayout 。如果這些views也是stack,那就一樣回傳該stack的forFirstBaselineLayoutforLastBaselineLayout 所回傳的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 = true

Stack view的properties改變時,Stack view也會有反應。

對於這些更動,也可以做出動畫效果,只要你把這些更動的code放到animate裡面去。但是對於加入view到arrangedSubviews來說,這不會觸發任何動畫效果。

    SC Tuan

    Written by

    SC Tuan

    iOS developer😘 / Web developer🤪/ anymore? 😎

    Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
    Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
    Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade