使用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; //排列視圖相鄰邊緣之間的距離。