使用UIStackView來簡化iOS的介面佈局

語言: CN / TW / HK

theme: smartblue highlight: xcode


我報名參加金石計劃1期挑戰——瓜分10萬獎池,這是我的第1篇文章,點選檢視活動詳情

前言

在過去iOS頁面佈局較為傳統,大多數人使用Frame或者AutoLayout來佈局,在iOS9以後,引入了UIStackViewUIStackView是用於線性佈局的控制元件,可以自動管理子檢視佈局,自動填充。它借鑑了前端的佈局演算法Flexbox,可以簡便地實現各種頁面佈局。

UIStackView雖然已經不是新控制元件了,但還是有很多同學並沒有使用起來。通常有時改別人的程式碼看到亂糟糟的佈局程式碼就有很多槽點。所以這也是寫這篇文章的目的所在,真的推薦大家使用StackView。事半功倍,省下來的時間摸魚不香嘛。

迴歸正題,不管是使用Frame或者AutoLayout來佈局,我們都需要對所有的控制元件的位置、大小進行設定,Frame需要指定位置佈局,AutoLayout需要指定約束佈局;而UIStackView佈局方式凸顯它的優勢在於不需要設定排列檢視(即子檢視)的位置,大小(不是必須的),而是通過自身的排列、分佈方式自動完成佈局。對比起來,使用UIStackView更高效,我們可以通過巢狀UIStackView快速完成各式各樣的佈局。

UIStackView佈局思想

UIStackView的初衷就是為了簡化的介面佈局,適用於列或行中佈局檢視集合。

StackView使用自動佈局(AutoLayout)來定位和設定其排列檢視的大小。StackView將第一個和最後一個排列的檢視與其沿堆疊軸的邊緣對齊。在水平堆疊中,這意味著第一個排列檢視的前緣被固定在StackView的前緣上,而最後一個排列檢視的後緣被固定在StackView的後緣上;在垂直堆疊中,頂部和底部邊緣分別固定在堆疊的頂部和底部邊緣上。

StackView會根據自身的佈局規則進行填充排列檢視。

A6967C3C-B348-4553-808C-2674285EAEBD.png

distribution:

distribution 即排列方式: - fill根據抗拉伸、壓縮優先順序填充(預設拉伸第一個排列檢視) - fillEqually在排列方向上的填充大小相同(即橫向佈局寬度相同,縱向佈局高度相同) - fillProportionally根據排列檢視的大小按比例填充 - equalSpacing均勻地填充檢視之間的間距 - equalCentering根據排列檢視中心點之間的相同間隔填充

alignment:

alignment即對齊方式:(垂直於排列方向) - fill填充排列檢視到StackView的可用空間 - topStackView的頂部排列(與之相似的是leading) - bottomStackView的尾部排列(與之相似的是trailing) - centerStackView的中間排列 - firstBaseline以第一個基準線排列 - lastBaseline以最後一個的基準線排列

如需設定排列檢視之間的間距可以通過設定space屬性,若是排列檢視之間的間距不同,可以使用方法指定某個排列檢視的間距(此方法iOS11以上使用),或者使用一個無用的view插入在檢視之間替代間隙,此view僅作為間距使用(使用xibStoryboard會經常使用此類方法,可以參照)。

當你真的瞭解UIStackView的這些佈局思想之後,你就會知道它能幫你解決很多繁瑣的佈局。(如一個多變的底部操作欄、一行大小各異的控制元件等等)

從上面的佈局思想中,不難看出,其實我們僅需要確定StackView的排列方向,以及排列方式、對其方式,就能大體上對整個排列檢視初步的佈局,而後在根據不同的檢視進行大小上的調整以及間距的調整即可。

使用UIStackView來自動佈局子檢視,你只需要每個子檢視關注自身的大小即可。

以此類操作欄為例,舉個栗子🌰:

截圖2022-08-31 00.09.43.png

截圖2022-08-31 00.19.43.png

先說說我們常用的佈局方式,可能還是會有一部分人會選擇Frame佈局,或者AutoLayout佈局。

但此類UI在設計之初,通常會有很多狀態、特徵,每一種狀態下,控制元件都會變化。

那麼Frame佈局在這種佈局容易變化的情況下,就顯得有非常的繁瑣,佈局程式碼非常的多,並且狀態很多的時候不好維護。 同樣AutoLayout也是如此,需要寫很多的更新佈局約束。

這個時候,借用UIStackView的思想,我們可以很簡單的實現這個佈局。每個控制元件只關注自身大小,不會對其他的空間產生依賴關係,在需要時顯示出來,不需要時隱藏起來。

我們先以文字輸入入口“說點什麼”小試牛刀。

UIStackView.png

下面就是用StackView佈局的效果。 截圖2022-09-05 22.46.35.png

這裡我是使用的xib結合StackView。如果我們平時使用的程式碼佈局,也可以使用程式碼結合StackView佈局,這樣也會減少很多程式碼量,可以自行腦補。

UIStackView用法

初始化

與其他控制元件一樣的初始化方式; - (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER; - (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;

當然也可以選擇專屬的初始化方式; - (instancetype)initWithArrangedSubviews:(NSArray<__kindof UIView *> *)views;

新增、刪除子檢視

- (void)addArrangedSubview:(UIView *)view; - (void)removeArrangedSubview:(UIView *)view;

排列方向

``` @property(nonatomic) UILayoutConstraintAxis axis;

typedef NS_ENUM(NSInteger, UILayoutConstraintAxis) {     UILayoutConstraintAxisHorizontal = 0, // 水平方向     UILayoutConstraintAxisVertical = 1 // 垂直方向 }; ```

佈局方式

``` @property(nonatomic) UIStackViewDistribution distribution;

typedef NS_ENUM(NSInteger, UIStackViewDistribution) {     UIStackViewDistributionFill = 0, //子檢視填充滿指定方向,優先拉伸第一個控制元件     UIStackViewDistributionFillEqually, //每個子檢視填充大小相等,     UIStackViewDistributionFillProportionally, //根據每個子視圖裡面內容的尺寸來進行填充操作     UIStackViewDistributionEqualSpacing, //每個子檢視之間的間距相等     UIStackViewDistributionEqualCentering, //每個子檢視中心直接的間距相等 } API_AVAILABLE(ios(9.0)); ```

對齊方式

``` @property(nonatomic) UIStackViewAlignment alignment;

typedef NS_ENUM(NSInteger, UIStackViewAlignment) { UIStackViewAlignmentFill, //水平:subView的上下和StackView的上下邊距 相等 垂直: subView的左右邊距和 StackView的所有相等 UIStackViewAlignmentLeading,//垂直有效 :左對齊 UIStackViewAlignmentTop = UIStackViewAlignmentLeading, // 水平有效 上對齊 UIStackViewAlignmentFirstBaseline,//水平有效,第一行基準線對齊。 UIStackViewAlignmentCenter, //中心基準線對齊 1.水平 高度中點對齊 2.垂直:寬度中點對齊 UIStackViewAlignmentTrailing, //垂直有效,右邊界對齊。 UIStackViewAlignmentBottom = UIStackViewAlignmentTrailing,// 水平有效 ,下邊界對齊。 UIStackViewAlignmentLastBaseline,//水平有效,最後一行基準線對齊。 } API_AVAILABLE(9_0); ```

間距

@property(nonatomic) NSInteger space; //排列檢視相鄰邊緣之間的距離。