原文:UIStackView Tutorial: Introducing Stack Views
最初教程由Jawwad Ahmad原創(chuàng)毡代。Kevin Colligan更新支持iOS 11、Xcode 9和Swift 4及志。
轉(zhuǎn)載注明:http://www.rockerhx.com/2018/08/21/2018-08-21-UIStackView-Tutorial/
iOS開(kāi)發(fā)當(dāng)中是不是遇到需要?jiǎng)討B(tài)添加或者刪除視圖元素的需求修噪。你是上基hub去嗨一遍第三方庫(kù)還是自行擼frame,或者用Auto Layout的約束更新着降。就這點(diǎn)破需求化漆,反正我是堅(jiān)決不選前兩者,即便是萬(wàn)不得已殊轴,最多也是更新約束(雖然Auto Layout的所見(jiàn)即所得極大縮短了開(kāi)發(fā)耗時(shí)衰倦,增加我擼貓的快樂(lè)時(shí)光,但不得不承認(rèn)我是很反感用代碼去改約束梳凛,可視化編碼還要去code約束耿币,腦子有彩嵝印H途堋Q徒印!)叛溢。
處理這類需求塑悼,總之一句話。
好在楷掉,現(xiàn)在大家伙應(yīng)該都只支持到iOS9了厢蒜,用上UIStackView
來(lái)處理這類需求簡(jiǎn)直是美滋滋,艾瑪真香~~~
在本教程中烹植,闊以了解UIStackView
如何提供一種簡(jiǎn)單的方式來(lái)處理水平或垂直布局斑鸦。還闊以了解如何通過(guò)使用對(duì)齊、分布和間距等屬性來(lái)獲取視圖草雕,以便自我調(diào)整方便自適應(yīng)巷屿。
本教程假定觀者基本熟悉Auto Layout。如果不熟悉墩虹,請(qǐng)移步傳送門Beginning Auto Layout嘱巾。
教程開(kāi)始
在這份UIStackView
教程中,我們將使用一個(gè)名為“Vacation Spots”的應(yīng)用程序诫钓。這個(gè)簡(jiǎn)單的應(yīng)用闊以展示度假地點(diǎn)的一些基礎(chǔ)信息旬昭。
不要方方臟臟的就開(kāi)始動(dòng)手,因?yàn)橛袔讉€(gè)問(wèn)題需要我們先用UIStackView
來(lái)處理下菌湃,而且比單獨(dú)使用自動(dòng)布局要簡(jiǎn)單得多问拘。首先下載這個(gè)UIStackView教程的入門項(xiàng)目。打開(kāi)項(xiàng)目并在iPhone模擬器上運(yùn)行惧所。你會(huì)看到一份度假地點(diǎn)清單场梆。
點(diǎn)擊London那一欄,進(jìn)入倫敦的信息視圖纯路。乍一看或油,好像還不錯(cuò),但有幾個(gè)問(wèn)題驰唬。
-
看看視圖底部的一排按鈕顶岸。他們目前的位置是固定的,所以他們不適應(yīng)屏幕寬度叫编。不信按Command - left將模擬器橫向來(lái)看看辖佣。
image - 點(diǎn)擊WEATHER旁邊的Hide按鈕。它成功地隱藏了文本搓逾,但是它沒(méi)有重新定位它下面的部分卷谈,留下一塊空白。
image - 可以改進(jìn)這些部分的展示順序霞篡。如果what to see部分正好位于why visit部分之后世蔗,而不是在兩者之間有weather部分端逼,這將會(huì)更合乎邏輯。
- 在橫屏模式下污淋,按鈕的底部離視圖的底部邊緣太近了一點(diǎn)顶滩。如果能減少元素之間的間距,那就更好看寸爆。
既然我們已經(jīng)羅列好了改進(jìn)工作礁鲁,那就動(dòng)手開(kāi)始吧!A薅埂仅醇!
打開(kāi)Main.storyboard。第一次這樣做時(shí)魔种,會(huì)要求您選擇初始設(shè)備視圖着憨。這個(gè)視圖在運(yùn)行時(shí)沒(méi)有效果,它針對(duì)不同的設(shè)備調(diào)整大小务嫡,也只是讓故事板的使用變得更加容易甲抖。iPhone 7或者8就行了。
你闊以隨時(shí)點(diǎn)擊故事板的操作區(qū)域的正下方心铃,選擇View As: iPhone 7 准谚,或者隨便改變也行。
現(xiàn)在打開(kāi)Spot Info View Controller看看去扣,這東一塊西一坨的顏色是什么鬼柱衔?
這些標(biāo)簽和按鈕的各種背景色,運(yùn)行時(shí)會(huì)被清除愉棱。在Storyboard中唆铐,它們只是視覺(jué)上的輔助,幫助顯示改變StackView的各種屬性將如何影響其嵌入視圖的布局奔滑。
你現(xiàn)在不用管這些艾岂,如果在運(yùn)行應(yīng)用程序的時(shí)候你真的想看到背景色,你可以在SpotInfoViewController內(nèi)部的viewDidLoad()中臨時(shí)注釋出以下幾行朋其。
// Clear background colors from labels and buttons
for view in backgroundColoredViews {
view.backgroundColor = UIColor.clear
}
此外王浴,所有的UI元素都使用了明確的占位內(nèi)容,以便鏈接屬性的時(shí)候更加明確方便梅猿,減小連錯(cuò)的可能性氓辣。
@IBOutlet weak var whyVisitLabel: UILabel!
叨逼叨完了,let’s get started!
第一次
使用stack view
第一件事是修復(fù)按鈕底部行之間的間距袱蚓。stack view
可以以各種方式沿其軸分布其視圖钞啸,其中一種方式是每個(gè)視圖之間的間距相等。
蘋果為了推廣新控件,我們可以直接把想要的處理的控件元素直接一鍵壓入stack view
体斩。打開(kāi)Spot Info View Controller故事版場(chǎng)景梭稚,按住Command
選中底部三個(gè)按鈕。
點(diǎn)擊右下角Show Document Outlin:
在左側(cè)控件集列表內(nèi)確認(rèn)下是不是選對(duì)了按鈕:
選中后硕勿,單擊故事板畫(huà)布右下角自動(dòng)布局工具欄中的“新建堆椛诨伲”按鈕:
按鈕將嵌入到新的堆棧視圖中:
現(xiàn)在出現(xiàn)了約束警告枫甲,
stack view
只是一種排版形式源武,是繼承自UIView,它也是需要約束來(lái)確定frame想幻,只有確定好自身的frame以后才能處理內(nèi)部控件布局粱栖。
在stack view
中嵌入控件時(shí),將刪除對(duì)其的任何約束脏毯。例如闹究,在將按鈕嵌入stack view
之前,Submit Rating按鈕的頂部有一個(gè)連接到Ratinglabel的垂直間距約束:
點(diǎn)擊選中Submit Rating按鈕可以看到它不在包含任何約束食店。
也闊以通過(guò)Size inspector (??5)來(lái)查看控件約束情況:
接下來(lái)我們需要為stack view
添加約束控制渣淤,如果整個(gè)頁(yè)面的控件太多導(dǎo)致不好選中某個(gè)控件,直接從左側(cè)的控件列表就好吉嫩。
另外有個(gè)小技巧价认,按住Shift
再單擊右鍵,Xcode會(huì)幫大忙自娩。
最后通過(guò)右下角的Auto Layout toolbar添加約束即可用踩。
保持Constrain to margins選中,然后添加如下約束忙迁。
Top: 20, Leading: 0, Trailing: 0, Bottom: 0
按照如圖所示輸入數(shù)字直接tab就好脐彩,簡(jiǎn)單而快速的添加約束才是敏捷開(kāi)發(fā)的首選。
控件之間現(xiàn)在看起來(lái)像是這樣姊扔,stack view
會(huì)拉伸第一個(gè)控件來(lái)填充空間惠奸。
決定stack view
內(nèi)部控件的分布屬性為distribution
。目前恰梢,它被設(shè)置為Fill
晨川,這意味著被包含的控件將沿著其軸完全填充。為了實(shí)現(xiàn)這一點(diǎn)删豺,stack view
將只拉伸其中一個(gè)視圖來(lái)填充額外的空間共虑;具體來(lái)說(shuō),它會(huì)拉伸content hugging
優(yōu)先級(jí)最低的視圖呀页,或者如果所有控件優(yōu)先級(jí)相同妈拌,它拉伸第一個(gè)視圖。
然鵝,我們只是向他們等距分布就好尘分。切換到Attributes inspector屬性編輯器猜惋,把Distribution的值從Fill改到Equal Spacing即可:
跑一下看看底部按鈕是不是等距分布了,無(wú)論是豎屏還是橫盤都是穩(wěn)穩(wěn)的培愁,不過(guò)還不夠完美著摔,當(dāng)你切換到小屏幕上,比如使用iPhone SE模擬器來(lái)跑一下看看定续。
[圖片上傳失敗...(image-b4cef2-1535097314156)]
好在蘋果也考慮到這種情況谍咆,我們改變Distribution屬性,從Equal Spacing改為Fill Proportionally私股,spacing值改為10摹察。
現(xiàn)在,在iPhone SE模擬器上再跑一次倡鲸,這次必須棒棒噠供嚎。
咋樣,第一次上手stack view
484真香峭状,so easy克滴。
在沒(méi)有stack view
察皇,你必須使用間隔視圖來(lái)占位外厂,每對(duì)按鈕之間有一個(gè)占位。要正確定位間隔視圖谜洽,您必須向所有間隔視圖添加等寬約束以及許多附加約束羔巢。
它看起來(lái)會(huì)像下面這樣望忆。為了在屏幕截圖中可見(jiàn),間隔視圖被賦予了淺灰色背景:
如果只是偶爾這么做一次其實(shí)還好竿秆,如果做多了启摄,小伙子身體可吃不消啊。特別是動(dòng)態(tài)添加視圖這種幽钢。比如隱藏例子中的WEATHER這種歉备。
現(xiàn)在我們只是簡(jiǎn)單的設(shè)置間距值,如果你想在某個(gè)視圖中設(shè)置特別的間距匪燕,在iOS11里提供的新的Api: setCustomSpacing:afterView蕾羊。
沙場(chǎng)老司機(jī)
經(jīng)過(guò)上面的演練,下面我們就能很輕松的把SpotInfoViewController里需要的結(jié)構(gòu)轉(zhuǎn)換成stack view
帽驯。
評(píng)級(jí)部分
選中評(píng)級(jí)RATING部分的兩個(gè)控件龟再。
接著還是同樣的點(diǎn)擊Stack按鈕將其嵌入到stack view
里。
這次我們只需要添加三個(gè)約束即可:
Top: 20, Leading: 0, Bottom: 20
spacing值這是為8
:
你可能會(huì)看到一個(gè)錯(cuò)位的視圖警告尼变,如下圖所示利凑,其中星星標(biāo)簽已經(jīng)超出了視圖的范圍(這種警告可能因?yàn)槟承゜eta版本的原因才會(huì)出現(xiàn),沒(méi)出現(xiàn)那就恭喜你繼續(xù)):
如果確定自己的約束是正確的,因?yàn)閄code某些版本的bug引起的視圖錯(cuò)位哀澈,直接用右下角的Refresh Layout按鈕矯正即可牌借。
現(xiàn)在看起來(lái)就正常了。
還原視圖
如果發(fā)現(xiàn)有的時(shí)候手殘割按,或者是處于實(shí)驗(yàn)性目的膨报,多上了stack view
,直接選中需要撤銷的控件适荣,按住Option现柠,左擊Stack選Unembed即可撤銷。
或者選中控件從菜單Editor \ Unembed撤銷也行束凑。
試試第一個(gè)垂直方式
現(xiàn)在我們來(lái)創(chuàng)建垂直方式的堆棧視圖晒旅,選中WHY VISIT和<whyVisitLabel>兩個(gè)文本控件栅盲。
Xcode會(huì)根據(jù)控件的分布排列來(lái)確實(shí)方向汪诉,就比如現(xiàn)在這樣,直接生成的就是垂直分布的堆棧視圖谈秫。
現(xiàn)在我們來(lái)確定約束扒寄,只需要把這部分的上左右約束都設(shè)置為0,只不過(guò)底部約束的對(duì)象是相對(duì)WEATHER控件拟烫,間距是20该编,一看即懂。
設(shè)置完后默認(rèn)對(duì)齊是左對(duì)齊硕淑,如下圖所示:
對(duì)齊屬性
對(duì)齊屬性確定堆棧視圖如何垂直于其軸布局课竣。對(duì)于垂直堆棧視圖,可以設(shè)置的對(duì)齊方式為:
- 填充·Fill
- 靠左對(duì)齊·Leading
- 居中對(duì)齊·Center
- 靠右對(duì)齊·Trailing
水平堆棧視圖對(duì)齊屬性的值略有不同:
水平布局的對(duì)齊方式分別為:
- 頂部對(duì)齊·Top
- 靠左對(duì)齊·Leading
- 底部對(duì)齊·Bottom
- 靠右對(duì)齊·Trailing
- 第一元素基準(zhǔn)線·FirstBaseline:意思是以第一個(gè)UI元素的的內(nèi)容基準(zhǔn)線為準(zhǔn)置媳,后面元素內(nèi)容以此基準(zhǔn)線對(duì)齊于樟。
- 最后元素基準(zhǔn)線·LastBaseline:意思是以最后一個(gè)UI元素的的內(nèi)容基準(zhǔn)線為準(zhǔn),前面元素內(nèi)容以此基準(zhǔn)線對(duì)齊拇囊。
大家闊以選中剛才設(shè)置好的垂直堆棧視圖迂曲,調(diào)整下對(duì)齊屬性看下變化就淺顯易懂了。
填充·Fill:
靠左對(duì)齊·Leading:
居中對(duì)齊·Center:
靠右對(duì)齊·Trailing:
跑起來(lái)看一下布局穩(wěn)當(dāng)冇問(wèn)題寥袭。
轉(zhuǎn)換“what to see”部分
這部分轉(zhuǎn)換和前面一節(jié)沒(méi)啥區(qū)別路捧,直接操作關(guān)鍵點(diǎn)即可。
- 首先選中WHAT TO SEE和<whatToSeeLabel>兩個(gè)文本元素传黄。
- 其次壓入堆棧視圖杰扫。
- 對(duì)齊方式選中填充·Fill。
- 最后設(shè)置好如下約束即可膘掰。
Top: 20, Leading: 0, Trailing: 0, Bottom: 20
設(shè)置完以后大概就長(zhǎng)這樣:
轉(zhuǎn)換“weather”部分
由于需要隱藏天氣章姓,所以這部分是比較難搞的。
我們先把這部分的所有元素都?jí)喝攵褩R晥D。先選中WEATHER文本欄和Hide按鈕啤覆,壓入水平堆棧視圖苍日,然后在按住Command選中下面的<weatherInfoLabel>文本欄部分,將水平堆棧視圖和這部分一起壓入垂直堆棧視圖窗声。
其實(shí)可以發(fā)現(xiàn)相恃,水平的堆棧視圖被Hide按鈕給頂開(kāi)了,如果覺(jué)得不喜歡笨觅,闊以選中底部對(duì)齊的方式來(lái)看看效果拦耐。
也還是不太滿意,那根據(jù)我的經(jīng)驗(yàn)见剩,要不就是修改水平堆棧視圖內(nèi)UI元素的約束優(yōu)先級(jí)杀糯,要不就是把Hide按鈕給拆出去,單獨(dú)處理約束苍苞。為了容易理解我們還是單獨(dú)處理固翰。
自行撤回到天氣部分的原始轉(zhuǎn)態(tài)。選中WEATHER和<weatherInfoLabel>兩個(gè)文本欄部分:
將其壓入垂直堆棧視圖:
然后設(shè)置好如下約束:
Top: 20, Leading: 0, Trailing: 0, Bottom: 20
對(duì)齊方式為填充:
因?yàn)?strong>Hide按鈕被拆出去羹呵,所以我們需要對(duì)其添加約束以保證按鈕位置的正確性骂际。因?yàn)樾枰O(shè)置約束的相對(duì)控件為WEATHER文本欄,所以我們還得再改造改造冈欢。
直接在控件列表欄選中WEATHER或使用組合拳Control-Shift-click:
壓入堆棧視圖:
對(duì)齊方式·Alignment選中為靠左對(duì)齊·Leading歉铝,排版·Axis確定為垂直·Vertical排版:
運(yùn)行看看是啥情況:
我頂你個(gè)肺,啥情況凑耻,按鈕跑偏了太示?實(shí)際上是我們拆出Hide按鈕之后忘記給設(shè)置約束。
按住Control選中Hide按鈕拖動(dòng)指向到WEATHER文本欄添加約束:
只要完成如下兩條約束香浩,間距和縱向位置的約束即可:
再重新跑一下类缤,這回就必須妥當(dāng)了。
堆椘埽化
在左側(cè)控件列表里選中我們之前所有做個(gè)堆椦椒牵化的控件:
全部壓入堆棧:
然后給最外層堆棧視圖添加約束,確保勾選上Constrain to margins镜盯,四邊邊距約束為0即可岸裙。
然后間隙·Spacing設(shè)置為20,對(duì)齊方式·Alignment設(shè)置為填充·Fill速缆。
再次運(yùn)行還是會(huì)有剛才那個(gè)屌問(wèn)題降允。
只要記住如果有外設(shè)相對(duì)控件的約束,只要堆椧彰樱化剧董,約束就會(huì)被干掉幢尚,只能再次添加了:
完善層級(jí)
最后我們需要把what to see的部分挪動(dòng)到weather部分上面去,最好的方式是在左側(cè)控件列表里拖動(dòng):
如果在Storyboard里直接去拖動(dòng)翅楼,很可能會(huì)因?yàn)檫x錯(cuò)控件元素而導(dǎo)致打亂結(jié)構(gòu)尉剩。
根據(jù)屏幕大小配置間隙
由于在橫屏的情況下,垂直方向上的空間比較珍貴毅臊,所以針對(duì)橫屏理茎,把間隙改為10比較穩(wěn)妥。
找到間隙·Spacing的左側(cè)的+號(hào)按鈕:
選擇Any Width > Compact Height管嬉,然后添加·Add Variation:
給wAny hC欄添加值為10即可:
最后運(yùn)行一下看看皂林,包括橫屏(?←)也看看,484看起來(lái)比較舒爽了蚯撩。
動(dòng)畫(huà)
當(dāng)前的隱藏和呈現(xiàn)動(dòng)畫(huà)效果看起來(lái)有木有點(diǎn)生硬础倍,感覺(jué)怪怪的。我們還是添加點(diǎn)動(dòng)畫(huà)讓細(xì)節(jié)看起來(lái)比較絲滑為妙胎挎。
打開(kāi)SpotInfoViewController.swift沟启,定位到updateWeatherInfoViews(hideWeatherInfo:animated:)
方法,現(xiàn)在看起來(lái)長(zhǎng)這樣:
weatherInfoLabel.hidden = shouldHideWeatherInfo
替換成如下代碼即可:
if animated {
UIView.animate(withDuration: 0.3) {
self.weatherInfoLabel.isHidden = shouldHideWeatherInfo
}
} else {
weatherInfoLabel.isHidden = shouldHideWeatherInfo
}
找到@IBAction func weatherHideOrShowButtonTapped(_ sender: UIButton)
方法呀癣,替換成如下代碼:
@IBAction func weatherHideOrShowButtonTapped(_ sender: UIButton) {
let shouldHideWeatherInfo = sender.titleLabel!.text! == "Hide"
updateWeatherInfoViews(hideWeatherInfo: shouldHideWeatherInfo, animated: shouldHideWeatherInfo)
shouldHideWeatherInfoSetting = shouldHideWeatherInfo
}
這樣跑一下看美浦,隱藏起來(lái)就不會(huì)辣么尬了弦赖。项栏。。
寫在最后
一切照舊蹬竖,懶癌患者或者閱讀理解不過(guò)關(guān)的胖友闊以直接下載最終代碼沼沈。
有關(guān)UIStackView
的使用就到這里,關(guān)于使用UIStackView
做高級(jí)動(dòng)畫(huà)是稍稍有點(diǎn)復(fù)雜的币厕,以后有空還是需要專門開(kāi)一篇來(lái)做介紹列另。