認(rèn)識UIStackView

前言

首先,我們通過下面這張圖片引出今天的主角

大家看到了什么,是愛嗎?
不,這不是愛,不是愛,是滿滿的‘愁緒’???
此刻,面對這一堆約束,或許你的內(nèi)心是淡然的,那么,如果情況是這堆約束里面存在一連串的Conflicting Constraints呢,是否感到崩潰。雖然對于這些警告和沖突,系統(tǒng)會有相應(yīng)的提示,但錯綜復(fù)雜的約束會讓你依然無從下手。一般情況下,對于修復(fù)類似稍微復(fù)雜的自動布局環(huán)境,如果你腦海里沒有清晰的約束結(jié)構(gòu),個人建議還是重新來過,這樣一般情況下會比你扯著頭皮去解析來得省時省力得多。當(dāng)然,如果使用UIStackView,我們就不必再為這成串的約束苦惱了,UIStackView能夠自動管理它內(nèi)部子控件的約束,從而會為我們省去大部分的手動約束。

UIStackView介紹

UIStackView 是 iOS 9 中新增的一個控件,它繼承于UIView,用來管理一行或一列視圖的布局。UIStackView新增了幾個屬性,這些屬性就是子視圖布局規(guī)則。一旦UIStackView的這些屬性發(fā)生變化,它的arrangedSubviews就會按照規(guī)則重新排布。只要我們掌握這些規(guī)則,就可以管理視圖布局了。如果能再稍加靈活運(yùn)用,有時候我們甚至能輕松實現(xiàn)一些精妙布局。

這里說明下UIStackView的arrangedSubviews數(shù)組屬性,它是subViews數(shù)組的子集合,我覺得有必要給大家重復(fù)下官方的幾條說明:

  • 無論何時 stackView添加了一個視圖到它的 arrangedSubviews 數(shù)組,其也將把這個視圖作為子視圖添加,如果還未添加的話。
  • 無論何時一個子視圖從 stackVierw中被移除,那么 stackView也會將其從 arrangedSubviews 數(shù)組中移除。
  • 從 arrangedSubviews 移除一個視圖并不會將其作為子視圖移除。stack 視圖將不再管理該視圖的尺寸和位置,但是該視圖仍將是視圖結(jié)構(gòu)的一部分,并且當(dāng)其可見的情況下仍會被渲染到屏幕上。

以下是官方對StackView的布局屬性解釋:

  • axis(軸向) 屬性決定了 stack 的朝向,只有垂直或水平;
  • distribution(分布) 屬性決定了其管理的視圖在沿著其軸向上的布局;
  • alignment(對齊) 屬性決定了其管理的視圖在垂直于其軸向上的布局;
  • spacing(空隙) 屬性決定了其管理的視圖間的最小間隙;
  • layoutMarginsRelativeArrangement 屬性決定了 stack 視圖平鋪其管理的視圖時是否要參照它的布局邊距;
  • baselineRelativeArrangement 屬性決定了 stack 視圖平鋪其管理的視圖時是否要參照它的布局邊距;

下圖是官方對alignment和distribution以及Spacing三個屬性的圖解:

這里附上我制作的一個 示例Demo,里面主要使用UIStackView和sizeclass完成了Demo主頁與StackView屬性展示的iPhone橫豎屏的適配,小伙伴們可以通過在屬性界面調(diào)整StackView的屬性來觀察屬性變化對StackView的布局影響。

UIStackViewDistribution和UIStackViewalignment

axis、spacing、distribution和alignment是比較重要的4個屬性,他們都能給布局帶來明顯的變化。axis和spacing屬性作用單一,通過屬性解釋或者通過視圖簡單觀察我們就能理解他們的作用。distribution和alignment這兩個屬性相對而言更具靈活性,也更具有難度,尤其是二者的結(jié)合使用。
我們先來看一下這兩個屬性的定義

public enum UIStackViewDistribution : Int {
    case fill
    case fillEqually
    case fillProportionally
    case equalSpacing
    case equalCentering
}
public enum UIStackViewAlignment : Int {
    case fill
    case leading
    public static var top: UIStackViewAlignment { get }
    case firstBaseline
    case center
    case trailing
    public static var bottom: UIStackViewAlignment { get }
    case lastBaseline 
}

這里我介紹一下distribution的各類模式(圖片來自官方文檔):

  • UIStackViewDistributionFill

將arrangedSubviews填充滿整個StackView,他們之間的間隙等于spacing大小。如果減去所有的spacing,所有arrangedSubview的固有尺寸(intrinsicContentSize)之和不能填滿StackView,那么就按照Hugging的優(yōu)先級將其拉伸。反之,如果超出StackView的尺寸則按CompressionResistance的優(yōu)先級壓縮。如果優(yōu)先級相同,就按排列順序來拉伸或壓縮。

  • UIStackViewDistributionFillEqually

每個arrangedSubview沿axis方向的長度相等,等于StackView沿axis長度減去spacing之和除以arrangedSubviews個數(shù)。

  • UIStackViewDistributionFillProportionally

根據(jù)arrangedSubview的intrinsicContentSize,將StackView沿axis方向的長度減去spacing之和按比例分配給arrangedSubviews。

  • UIStackViewDistributionEqualSpacing

先按arrangedSubviews的intrinsicContentSize布局,然后余下的空間均分為spacing,如果spacing小于StackView設(shè)置的spacing,則按照CompressionResistance的優(yōu)先級來壓縮arrangedSubviews。

  • UIStackViewDistributionEqualCentering

令arrangedSubviews的中心點之間的距離相等,且spacing大于等于StackView設(shè)置的spacing(每兩個arrangedSubview之間的spacing可能不相等)。如果spacing小于StackView設(shè)置的spacing,則按照CompressionResistance的優(yōu)先級來壓縮arrangedSubviews。

對于alignment屬性,它決定的是垂直axis方向的對齊方式,從命名上我們也能看出個大概。有兩點我們需要了解一下:

1、Top與Leading模式相同,Bottom與Trailing模式相同,從UIStackViewAlignment的定義中我們也能隱約猜出。

2、FirstBaseline和LastBaseline這兩種模式是讓arrangedSubviews分別按照firstBaseline和lastBaseline對齊。只能出現(xiàn)在axis為水平的StackView中。其實這兩種alignment在垂直StackView中也是可以設(shè)置的,只是顯示時有些怪異罷了)

storyboard添加StackView

方法1:從對象庫中拖拽UIStackView到storyboard中,然后往內(nèi)部扔控件(UIView或其子類)就可以了。

方法2:選擇storyboard中的控件,可以用“ommand鍵 + 單擊”進(jìn)行多選,然后點擊下方的stack按鈕,這樣選中的控件就會被放入一個StackView中


StackView使用技巧

1、嵌套
只要嵌套好UIStackView,就可以用很少的約束達(dá)到自動布局界面的目的。

2、增量排版
由于我們StackView中我們常用的alignment模式為Fill,因此會經(jīng)常出現(xiàn)arrangedSubView被拉伸變形的情況,也許有人會想到給控件添加寬高約束,但這會與StackView自動生成的約束突出,并不能達(dá)到目的。這里我們可以將這個arrangedSubView裝進(jìn)一個StackView中,然后添加透明的UIView,通過添加UIView的寬度或者高度約束從而達(dá)到約束arrangedSubView的目的。

3、結(jié)合sizeClass
查看SatckView的Attributes Inspector,我們會發(fā)現(xiàn)StackView的幾個主要屬性都是可以設(shè)置sizeclass模式的,這對我們的屏幕適配將會大有助益。再加上約束的sizeClass,靈活性可以想象。

布局實踐

下面我就帶大家感受下Demo中是如何利用這三點技巧的。

首先附上Reveal中Demo首頁視圖結(jié)構(gòu)層次:

在這個頁面中,我粗略計算了下大約一共有14條約束,這其中還包括了橫屏的適配,視圖布局的優(yōu)化。如果是粗糙布局,我們甚至可以用小于6條的約束完成布局。那么,問題來了,如果這些視圖是純粹用自動布局排版的,請問約束數(shù)目幾何?

為了方便說明,我將Demo中的主頁視圖分為5個區(qū)域:

制作Demo時我采用的是整體到局部的思路,即先限定容器(最外層StackView)的大小,然后添加子容器(區(qū)域StackView),根據(jù)需求添加相應(yīng)的內(nèi)容以及判斷是否還需要子容器。就拿區(qū)域1來說,它的結(jié)構(gòu)是這樣的:

這里添加UIView就是采用了增量排版,因為如果label的文字直抵屏幕邊距影響美觀,所以我讓UIView為其創(chuàng)造邊距。再如區(qū)域5的泊學(xué)logo,為了不使其被拉伸,我在兩邊添加了兩個UIView,從而達(dá)到約束其寬度的目的。

針對橫屏適配,我主要采用了以下幾條策略

1、設(shè)置不同sizeClass模式下區(qū)域1的高度

2、區(qū)域2由三個模塊組成(button+兩個StackVIew),兩個StackView寬度相同,wAny | hAny 模式下為0。在wAny | hCompact模式下添加Button的寬度約束,StackView就擁有了寬度。

3、通過高度約束,在wAny | hCompact模式下隱藏區(qū)域3,因為我們將它放在區(qū)域2顯示了。

4、調(diào)整不同sizeClass下的spacing(橫屏下高度資源不容浪費(fèi))

最后我們的視圖在橫屏下是這樣的

StackView帶給我們的好
大量減少自定義約束以及增強(qiáng)靈活度:曾經(jīng)的約束就像用繩子將一個個控件捆在了一起,而現(xiàn)在的StackView更像一個個盒子將控件裝了起來,我們可以用大盒子裝小盒子,也可以將這個盒子的東西轉(zhuǎn)移到另一個盒子。盒子的存在使得我們只要掌握最大的那個盒子,盒子內(nèi)的器件也終將被我們掌握,畢竟盒子內(nèi)的器件無論位置怎么變動,它始終存在于盒子之中。

層次關(guān)系: UIStackView的嵌套以及對arrangedSubViews的排列使得界面控件之間的結(jié)構(gòu)層次更為清晰。代碼布局:有了StackView,代碼添加自動布局明顯也方便了許多,無需像以前那樣為每一條約束寫一串語句,只要把視圖分層包裝好,通過設(shè)置UIStackView的屬性,就可以自動布局整個界面了。總結(jié)

最后,我們在使用stackView的時候,盡量通過設(shè)置stackView的屬性來完成布局,盡可能的減少內(nèi)部子控件的約束,這樣可以讓視圖更具靈活性,也避免了許多約束沖突,增強(qiáng)可維護(hù)性。靈活利用StackView的嵌套,再添加一點我們的小思維,讓視圖如同被賦予生命一般躍動起來吧!
stack確實給我們的開發(fā)帶來種種益處,我們沒有理由不去學(xué)習(xí)掌握它。而且如果大家想在iOS9以下系統(tǒng)使用UIStackView也并不是沒有可能,sunny大神和他的同事利用運(yùn)行時制作的 FDStackView 這個框架讓你感覺仿佛可以在iOS6+環(huán)境下直接使用UIStackView一樣,而且支持Interface build。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 翻譯自“Auto Layout Guide”。 1 入門 1.1 理解自動布局 自動布局根據(jù)視圖層級結(jié)構(gòu)中視圖上的...
    lakerszhy閱讀 3,922評論 3 26
  • 我的博客, 各位看官有時間賞光 UIStackView UIStackView介紹 隨著autolayout的推廣...
    VIC_LI閱讀 9,644評論 0 17
  • 這是一篇挺老的文章,主要就是介紹在iOS9時推出的控件UIStackView。我發(fā)現(xiàn)網(wǎng)上的資料并不算多,而AppC...
    Liberalism閱讀 11,248評論 2 26
  • 距離iOS9發(fā)布已經(jīng)接近一年了,我們即將引來新的iOS 10,為何在這個時候來介紹iOS9中新引入的一個布局組件呢...
    CZ_iOS閱讀 7,813評論 9 59
  • UIStackView to resize/layout 自適應(yīng)、適配、布局這幾個關(guān)鍵詞一直伴隨著iOS開發(fā),從以...
    萌新小透明閱讀 2,235評論 1 1

友情鏈接更多精彩內(nèi)容