接受新時(shí)代的UIStackView

距離iOS9發(fā)布已經(jīng)接近一年了格二,我們即將引來新的iOS 10,為何在這個(gè)時(shí)候來介紹iOS9中新引入的一個(gè)布局組件呢寸齐?猶如當(dāng)年的AutoLayout剛推出來一樣,一來文檔少、二來操作繁瑣济赎,最重要的要是兼容之前的系統(tǒng),用新技術(shù)擼一邊等于是多做工记某。如今在LinkdedIn已經(jīng)要求從iOS8開始的時(shí)代(QQ/微信/微博/淘寶均要求>=iOS7)司训,可以預(yù)見iOS10發(fā)布后不久的將來iOS9也將成為最低要求,其帶來的一些高效率工具(比如這里要介紹的UIStackView)也必將成為主流液南。

如果有Android相關(guān)開發(fā)經(jīng)驗(yàn)壳猜,或者從Android開發(fā)轉(zhuǎn)到iOS開發(fā),會(huì)發(fā)現(xiàn)Android4就引入的可以解決多屏幕適配Linerlayout/RelativeLayout在iOS中找不到對(duì)應(yīng)的工具滑凉,而在iOS9中统扳,Apple就為我們添加了這樣的一個(gè)工具,它就是UIStackView畅姊。首先不要被名字所迷惑咒钟,以為是和UICollectionView、UITableView一樣一般作為最外層的容器View若未,雖然他也確實(shí)就是個(gè)容器View朱嘴。其實(shí)用一句話就可以概況它的本質(zhì):“自動(dòng)對(duì)一組橫向或豎向view集布局的容器view”。如果熟悉HTML的話粗合,可以類比"<div />" 不帶block屬性的就是橫向布局萍嬉,帶block組合的就是豎向布局。

UIStackView內(nèi)部是為其托管的子View添加Autolayout來實(shí)現(xiàn)其自動(dòng)布局的隙疚,所以要想更熟練的使用UIStackView壤追,最好能對(duì)AutoLayout有一定的理解,當(dāng)然供屉,如果對(duì)AutoLayout還不太熟悉大诸,也沒有關(guān)系,UIStackView的目的就是為使用者封裝這些復(fù)制的約束關(guān)系而存在的贯卦,只要看下面文章资柔,相信也能將UIStackView這一高效率組件運(yùn)用到自己的工程中。

和UICollectionView撵割、UITableView不一樣的是贿堰,UIStackView沒有繼承與UIScrollview而是直接繼承與UIView,所以對(duì)于超出屏幕的內(nèi)容啡彬,還需要自己用UIScrollView進(jìn)行交互布局羹与。雖然UIStackView是繼承與UIView故硅,但是卻沒有繼承UIView的渲染功能,所以UIStackView是沒有UI的纵搁,也就是不顯示本身的吃衅。所以類似“backgroundColor”的界面屬性就無效了,同時(shí)重寫 layerClass, drawRect:甚至drawLayer:inContext:都是無效的腾誉。UIStackView是一個(gè)純粹的容器View徘层。

1. 最簡(jiǎn)單的一橫和一豎

說了這么多,到底要怎么使用呢利职?先來看個(gè)例子趣效,文中Demo都可以在Github找到:

signal_demo

signal_demo_plan

在上面的例子中,包含兩個(gè)StackView布局(兩個(gè)淺藍(lán)色框):一個(gè)上面的橫向的猪贪,一個(gè)下面豎向的跷敬。

橫向的方框中有三個(gè)子圖片,豎向的方框中有四個(gè)子元素热押。這樣的布局要如何實(shí)現(xiàn)呢西傀?其實(shí)很簡(jiǎn)單,先來看在IB中的操作桶癣,就像平常一樣先拖三個(gè)圓圈圖片排成一排拥褂,然后按住“Command”鍵,選中三個(gè)圖片鬼廓,然后點(diǎn)擊Xcode的

“Editor” -> "Embed In" -> "Stack View"

會(huì)發(fā)現(xiàn)肿仑,三個(gè)圖片的位置被改動(dòng)了,緊貼在一起碎税,并且在IB中尤慰,看到三個(gè)圖片被一個(gè)新的“Stack View”包含了:

ib_signal_layer

其實(shí)到這里就完成了一半需求了:有個(gè)容器View來管理一排子view。 現(xiàn)在在把目光放到IB的屬性界面雷蹂,來完成另一半
需求:

ib_signal_attr

設(shè)置這樣的屬性伟端,Aligent為“Fill”,Distribution為“Equal Spacing”匪煌,Space為“8”责蝠。表示: 所有的子視圖豎直
方向填充滿StackView,也就是子view可能被拉伸到和StackView等高萎庭,每個(gè)子View之間等距間隔8 point霜医。有了這樣兩個(gè)約束也就能固定子View的布局了,從而實(shí)現(xiàn)對(duì)子View的AutoLayout驳规。

當(dāng)然除了對(duì)已有元素通過“Embed In”加入StackView肴敛,也可以從IB右下角拖一個(gè)StackView到面板中,比如這里拖動(dòng)一個(gè)豎直方向的“?StackView”到面板中,然后再從圖片里面拖幾個(gè)橫著的圖片到這里的豎直的StackView中医男,同時(shí)設(shè)置space為“8”砸狞,就可以完成上面的布局了。當(dāng)然镀梭,這里需要對(duì)StackView自己做一些AutoLayout的設(shè)置刀森,從而確定容器的布局(也就是位置和大小)报账,然后StackView才能結(jié)合屬性確定其內(nèi)部子View的布局研底。

當(dāng)然除了使用IB也可以通過代碼來創(chuàng)建StackView:

UIImageView *star1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"dynamic_start"]];
star1.contentMode = UIViewContentModeScaleToFill;
UIImageView *star2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"dynamic_start"]];
star2.contentMode = UIViewContentModeScaleToFill;
UIImageView *star3 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"dynamic_start"]];
star2.contentMode = UIViewContentModeScaleToFill;
UIStackView *starStackView = [[UIStackView alloc] initWithArrangedSubviews:@[star1, star2, star3]];
starStackView.axis = UILayoutConstraintAxisHorizontal;
starStackView.alignment =  UIStackViewAlignmentFill
starStackView.distribution = UIStackViewDistributionEqualSpacing
starStackView.spacing = 8.0

上面的代碼也實(shí)現(xiàn)了一排橫向布局,其中axis控制了水平方向還是豎直方向笙什,alignment飘哨、distribution以及spacing對(duì)應(yīng)IB里面的屬性設(shè)置胚想。

2. 修改屬性定制StackView

在上面的IB屬性欄中琐凭,可以看到,StackView的屬性其實(shí)少的可憐浊服,圖中就四個(gè)可以設(shè)置(其實(shí)也確實(shí)就這四個(gè)加上個(gè)子view的數(shù)組)统屈。這里的"Axis"比較好理解,就是控制是一橫還是一豎牙躺,容器的方向愁憔。同樣的"Spacing"也比較好理解,就是壘在一起的子view之間的距離孽拷。但是這個(gè)"Alignment"和"Distribution"又是什么呢吨掌?我們來通過例子中的"Attr" Tab中的按鈕選項(xiàng)來看:

attr_demo_1

attr_demo_2

這里第一排按鈕是Alignment,第二排按鈕是Distribution脓恕∧に危可以運(yùn)行Demo并體會(huì)不同。

這里Alignment主要控制垂直于StackView方向上的對(duì)其屬性炼幔,二Distribution則是控制在StackView延展方向的填充屬性:

attr

下面看看總共都有哪些Alignment和Distribution秋茫。

Alignment

Alignment 意義 效果
UIStackViewAlignmentFill 在StackView垂直方向上拉伸所有子view,使得填充完StackView
align_fill
UIStackViewAlignmentLeading 在StackView垂直方向上按照子view的leading edge對(duì)齊
align_leading
UIStackViewAlignmentTop 等效UIStackViewAlignmentLeading,用于豎向Stackview
align_top
UIStackViewAlignmentFirstBaseline 在StackView垂直方向上按照子view 的first baseline對(duì)其乃秀,僅適用于水平方向StackView
align_first_baseline
UIStackViewAlignmentCenter 在StackView垂直方向上按照子View的中心線對(duì)其
align_center
UIStackViewAlignmentTrailing 在StackView垂直方向上按照子View的trailing edge對(duì)齊
align_trailing
UIStackViewAlignmentBottom 等效UIStackViewAlignmentTrailing,用于豎向Stackview
align_bottom
UIStackViewAlignmentLastBaseline 在StackView垂直方向上按照子view 的last baseline對(duì)齊肛著,僅適用于水平方向StackView
align_last_baseline

Distribution

Distribution 意義 效果
UIStackViewDistributionFill 在StackView延伸方向上縮放子View使得子View能填充完StackView,子View的縮放順序依賴于其hugging優(yōu)先級(jí)跺讯,如果相等的話枢贿,則按照index順序
dist_fill
UIStackViewDistributionFillEqually 在StackView延伸方向上將每個(gè)子View都拉伸成一樣長
dist_fill_equally
UIStackViewDistributionFillProportionally 在StackView延伸方向上將根據(jù)子View的內(nèi)容進(jìn)行縮放
dist_fill_proportionally
UIStackViewDistributionEqualSpacing 在StackView延伸方向上將子View中間隔相等的空白進(jìn)行縮放,如果子View不夠大刀脏,則用空白填充開始部分局荚,如果子View過大,則根據(jù)hugging順序縮放火本,如果相等的話危队,則按照index順序
dist_equal_spacing
UIStackViewDistributionEqualCentering 在StackView延伸方向上將子View的中線線聪建,等距進(jìn)行縮放,如果子View不夠大茫陆,則用空白填充開始部分金麸,如果子View過大,則根據(jù)hugging順序縮放簿盅,如果相等的話挥下,則按照index順序
dist_equal_centering

雖然上面羅列出來各個(gè)屬性的作用,但是可能還是不夠具體桨醋,這個(gè)還需要結(jié)合Demo或者自己在實(shí)際代碼中進(jìn)行設(shè)置來體驗(yàn)

3. 嵌套布局

上面的一橫一豎的例子棚瘟,在使用的時(shí)候,其實(shí)不用StackView也是非常容易用AutoLayout布局的喜最,那么怎么樣來提現(xiàn)StackView的優(yōu)勢(shì)呢偎蘸?如果把一橫一豎進(jìn)行各種組合,這樣就能像網(wǎng)頁設(shè)計(jì)中的"<div />"一樣進(jìn)行豐富的布局了瞬内,假設(shè)一個(gè)這樣的布局:

nested_design

可以將其分解成各種橫豎的組合迷雪,從而得到如下的一個(gè)效果圖

nested_demo

nested_effect

在IB中可以很容易的拖拽實(shí)現(xiàn)StackView的嵌套,這里僅僅對(duì)最外層的StackView做了大小和位置設(shè)置虫蝶,其他子View均是由StackView來自動(dòng)控制的章咧。

右上角是一個(gè)豎向的StackView(假設(shè)名字為A)并設(shè)置Aligment為"Leading/Top",Distrubition為"Equal Centering"。然后以A為整體能真,在和圖片一起放到一個(gè)橫向的StackView(假設(shè)名字為B)中赁严,并設(shè)置Aligment為"Top",Distrubition為"Fill Equal"。最后將這個(gè)B和下面的大圖和文字TextView放到一個(gè)豎向的StackView中粉铐,并設(shè)置Aligment為"Leading/Top",Distrubition為"Fill Equal"疼约。這樣就通過嵌套StackView完成了一個(gè)復(fù)雜布局了,和要多每個(gè)子view都要設(shè)置AutoLayout相比是不是很簡(jiǎn)單秦躯。

4. 不用datasource的動(dòng)態(tài)布局

在使用UITableView或者UICollectionView等容器View的時(shí)候忆谓,通常都會(huì)有個(gè)datasource來動(dòng)態(tài)的填充其中的容納的內(nèi)容,但是同樣作為容器View的StackView卻沒有這樣的datasoure踱承,他就只用一個(gè)數(shù)組和“add/remove”方法來管理其容納的子view倡缠。

最開始的 - (instancetype)initWithArrangedSubviews:(NSArray<__kindofUIView *> *)views展示了怎么用一個(gè)數(shù)組初始化StackView了,數(shù)組中的view會(huì)依照其順序添加到容器View StackView中茎活,其包括兩個(gè)部分昙沦,一部分是將布局托管給StackView設(shè)置,另一方面會(huì)調(diào)用addSubView载荔,添加到StackView中顯示盾饮。

如果需要添加新的子View可以調(diào)用:

- (void)addArrangedSubview:(UIView *)view 

將一個(gè)新的view托管給StackView進(jìn)行布局,并addSubView進(jìn)行顯示。這里新的view會(huì)別追加到已有子view的后面丘损,如果想插入到中間普办,可以使用:

  • (void)insertArrangedSubview:(UIView *)view
    atIndex:(NSUInteger)stackIndex

如果不想顯示一個(gè)子view要怎么操作呢?當(dāng)然調(diào)用子View的“removeFromSuperview”,但是這樣就夠了么徘钥?上面說了兩步衔蹲,這個(gè)remove只對(duì)應(yīng)了其中的顯示,但是并沒有消除其布局的影響呈础,所以還要調(diào)用StackView的:

- (void)removeArrangedSubview:(UIView *)view

取消其對(duì)布局的影響舆驶。

最后看個(gè)例子,點(diǎn)擊“贊”會(huì)增加星星而钞,點(diǎn)擊“貶”會(huì)減少星星數(shù)目:

dynamic_demo

布局很簡(jiǎn)單沙廉,主要是操作StackView的增減子view:

@interface DynamicVC ()

@property (weak, nonatomic) IBOutlet UIStackView *starStackView;

@end

@implementation DynamicVC

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder  {
    if ( self = [super initWithCoder:aDecoder]) {
        self.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Dynamic" image:[UIImage imageNamed:@"test"] selectedImage:[UIImage imageNamed:@"test"]];
    }
    return  self;
}
- (IBAction)onUp:(id)sender {
    UIImageView *star = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"dynamic_start"]];
    star.contentMode = UIViewContentModeScaleToFill;
    [self.starStackView addArrangedSubview:star];

    [UIView animateWithDuration:1.0 animations:^{
        [self.starStackView layoutIfNeeded];
    }];
}

- (IBAction)onDown:(id)sender {
    UIImageView *star = [self.starStackView.arrangedSubviews lastObject];
    [self.starStackView removeArrangedSubview:star];
    [star removeFromSuperview];
    [UIView animateWithDuration:1.0 animations:^{
        [self.starStackView layoutIfNeeded];
    }];
}

5. 總結(jié)

在當(dāng)前的產(chǎn)品中,可能會(huì)考慮到兼容以前的版本臼节,不會(huì)考慮用UIStackView在重新一遍以前的邏輯撬陵,畢竟上面舉例的場(chǎng)景,其實(shí)不用UIStackView官疲,也是有很成熟的
方法進(jìn)行布局袱结,而且基本都被大家運(yùn)用在產(chǎn)品中亮隙,經(jīng)過了生產(chǎn)環(huán)境的驗(yàn)證途凫。但是了解了UIStackView,在日后做Demo的時(shí)候溢吻,可以為布局節(jié)省很多精力维费,并且也可以
為未來iOS9成為最低配時(shí)積累經(jīng)驗(yàn),在未來的產(chǎn)品中用更高效的工具進(jìn)行布局促王,節(jié)省耗在布局上的時(shí)間犀盟。所以還是推薦大家在iOS10即將出生之際學(xué)習(xí)下這個(gè)新時(shí)代的
布局工具。

UIStackView其實(shí)很好理解蝇狼,就是一橫一豎的關(guān)系阅畴,但是通過調(diào)節(jié)其屬性(UIStackViewDistribution和UIStackViewAlignment)可以透明的運(yùn)用Auto?Layout帶來強(qiáng)大的自動(dòng)布局功能。通過自己多聯(lián)系嘗試不同屬性的組合迅耘,積累經(jīng)驗(yàn)贱枣,這樣才能在需要的時(shí)候,快速的用UIStackView處理以前需要很多步驟
(比如各種Autolayout約束颤专、用UICollectionView或者UITableView)才能搞定的布局纽哥。

另外UIStackView是對(duì)AutoLayout的一個(gè)封裝,其本身是和AutoLayout不沖突的(實(shí)際上就是新增了幾條約束)栖秕,所以熟練使用AutoLayout春塌,并和UIStackView配合,能夠?qū)崿F(xiàn)大量復(fù)雜的布局效果。

參考

  1. iOS 9: Getting Started with UIStackView
  2. UIStackView Class Reference
  3. Auto Layout Guide -- Stack Views
  4. iOS 9界面適配利器:詳解Xcode 7的新特性UIStackView
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末只壳,一起剝皮案震驚了整個(gè)濱河市俏拱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吼句,老刑警劉巖彰触,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異命辖,居然都是意外死亡况毅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門尔艇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尔许,“玉大人,你說我怎么就攤上這事终娃∥独龋” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵棠耕,是天一觀的道長余佛。 經(jīng)常有香客問我,道長窍荧,這世上最難降的妖魔是什么辉巡? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蕊退,結(jié)果婚禮上郊楣,老公的妹妹穿的比我還像新娘。我一直安慰自己瓤荔,他們只是感情好净蚤,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著输硝,像睡著了一般今瀑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上点把,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天橘荠,我揣著相機(jī)與錄音,去河邊找鬼愉粤。 笑死砾医,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的衣厘。 我是一名探鬼主播如蚜,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼压恒,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了错邦?” 一聲冷哼從身側(cè)響起探赫,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎撬呢,沒想到半個(gè)月后伦吠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡魂拦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年毛仪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芯勘。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡箱靴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出荷愕,到底是詐尸還是另有隱情衡怀,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布安疗,位于F島的核電站抛杨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏荐类。R本人自食惡果不足惜怖现,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望掉冶。 院中可真熱鬧真竖,春花似錦、人聲如沸厌小。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽璧亚。三九已至,卻和暖如春脂信,著一層夾襖步出監(jiān)牢的瞬間癣蟋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國打工狰闪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留疯搅,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓埋泵,卻偏偏與公主長得像幔欧,于是被迫代替她去往敵國和親罪治。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容