UIStackView上手教程

原文:UIStackView Tutorial: Introducing Stack Views
最初教程由Jawwad Ahmad原創。Kevin Colligan更新支持iOS 11、Xcode 9和Swift 4。
轉載注明:http://www.rockerhx.com/2018/08/21/2018-08-21-UIStackView-Tutorial/

iOS開發當中是不是遇到需要動態添加或者刪除視圖元素的需求。你是上基hub去嗨一遍第三方庫還是自行擼frame,或者用Auto Layout的約束更新。就這點破需求,反正我是堅決不選前兩者,即便是萬不得已,最多也是更新約束(雖然Auto Layout的所見即所得極大縮短了開發耗時,增加我擼貓的快樂時光,但不得不承認我是很反感用代碼去改約束,可視化編碼還要去code約束,腦子有病!!!)。
處理這類需求,總之一句話。


image

好在,現在大家伙應該都只支持到iOS9了,用上UIStackView來處理這類需求簡直是美滋滋,艾瑪真香~~~
在本教程中,闊以了解UIStackView如何提供一種簡單的方式來處理水平或垂直布局。還闊以了解如何通過使用對齊、分布和間距等屬性來獲取視圖,以便自我調整方便自適應。

本教程假定觀者基本熟悉Auto Layout。如果不熟悉,請移步傳送門Beginning Auto Layout

教程開始

image

在這份UIStackView教程中,我們將使用一個名為“Vacation Spots”的應用程序。這個簡單的應用闊以展示度假地點的一些基礎信息。
不要方方臟臟的就開始動手,因為有幾個問題需要我們先用UIStackView來處理下,而且比單獨使用自動布局要簡單得多。首先下載這個UIStackView教程的入門項目。打開項目并在iPhone模擬器上運行。你會看到一份度假地點清單。

image

點擊London那一欄,進入倫敦的信息視圖。乍一看,好像還不錯,但有幾個問題。

  • 看看視圖底部的一排按鈕。他們目前的位置是固定的,所以他們不適應屏幕寬度。不信按Command - left將模擬器橫向來看看。


    image
  • 點擊WEATHER旁邊的Hide按鈕。它成功地隱藏了文本,但是它沒有重新定位它下面的部分,留下一塊空白。
    image
  • 可以改進這些部分的展示順序。如果what to see部分正好位于why visit部分之后,而不是在兩者之間有weather部分,這將會更合乎邏輯。
  • 在橫屏模式下,按鈕的底部離視圖的底部邊緣太近了一點。如果能減少元素之間的間距,那就更好看。

既然我們已經羅列好了改進工作,那就動手開始吧!!!

打開Main.storyboard。第一次這樣做時,會要求您選擇初始設備視圖。這個視圖在運行時沒有效果,它針對不同的設備調整大小,也只是讓故事板的使用變得更加容易。iPhone 7或者8就行了。

image

你闊以隨時點擊故事板的操作區域的正下方,選擇View As: iPhone 7 ,或者隨便改變也行。
image

現在打開
Spot Info View Controller
看看,這東一塊西一坨的顏色是什么鬼?
image

這些標簽和按鈕的各種背景色,運行時會被清除。在Storyboard中,它們只是視覺上的輔助,幫助顯示改變StackView的各種屬性將如何影響其嵌入視圖的布局。

你現在不用管這些,如果在運行應用程序的時候你真的想看到背景色,你可以在SpotInfoViewController內部的viewDidLoad()中臨時注釋出以下幾行。

// Clear background colors from labels and buttons
for view in backgroundColoredViews {
    view.backgroundColor = UIColor.clear
}

此外,所有的UI元素都使用了明確的占位內容,以便鏈接屬性的時候更加明確方便,減小連錯的可能性。

@IBOutlet weak var whyVisitLabel: UILabel!

叨逼叨完了,let’s get started!

第一次

使用stack view第一件事是修復按鈕底部行之間的間距。stack view可以以各種方式沿其軸分布其視圖,其中一種方式是每個視圖之間的間距相等。
蘋果為了推廣新控件,我們可以直接把想要的處理的控件元素直接一鍵壓入stack view。打開Spot Info View Controller故事版場景,按住Command選中底部三個按鈕。

image

點擊右下角Show Document Outlin

image

在左側控件集列表內確認下是不是選對了按鈕:


image

選中后,單擊故事板畫布右下角自動布局工具欄中的“新建堆棧”按鈕:


image

按鈕將嵌入到新的堆棧視圖中:


image

現在出現了約束警告,stack view只是一種排版形式,是繼承自UIView,它也是需要約束來確定frame,只有確定好自身的frame以后才能處理內部控件布局。

stack view中嵌入控件時,將刪除對其的任何約束。例如,在將按鈕嵌入stack view之前,Submit Rating按鈕的頂部有一個連接到Ratinglabel的垂直間距約束:

image

點擊選中Submit Rating按鈕可以看到它不在包含任何約束。

image

也闊以通過Size inspector (??5)來查看控件約束情況:

image

接下來我們需要為stack view添加約束控制,如果整個頁面的控件太多導致不好選中某個控件,直接從左側的控件列表就好。

image

另外有個小技巧,按住Shift再單擊右鍵,Xcode會幫大忙。

image

最后通過右下角的Auto Layout toolbar添加約束即可。

image

保持Constrain to margins選中,然后添加如下約束。

Top: 20, Leading: 0, Trailing: 0, Bottom: 0

按照如圖所示輸入數字直接tab就好,簡單而快速的添加約束才是敏捷開發的首選。


image

控件之間現在看起來像是這樣,stack view會拉伸第一個控件來填充空間。

image

決定stack view內部控件的分布屬性為distribution。目前,它被設置為Fill,這意味著被包含的控件將沿著其軸完全填充。為了實現這一點,stack view將只拉伸其中一個視圖來填充額外的空間;具體來說,它會拉伸content hugging優先級最低的視圖,或者如果所有控件優先級相同,它拉伸第一個視圖。

然鵝,我們只是向他們等距分布就好。切換到Attributes inspector屬性編輯器,把Distribution的值從Fill改到Equal Spacing即可:

image

跑一下看看底部按鈕是不是等距分布了,無論是豎屏還是橫盤都是穩穩的,不過還不夠完美,當你切換到小屏幕上,比如使用iPhone SE模擬器來跑一下看看。
[圖片上傳失敗...(image-b4cef2-1535097314156)]

好在蘋果也考慮到這種情況,我們改變Distribution屬性,從Equal Spacing改為Fill Proportionallyspacing值改為10

image

現在,在iPhone SE模擬器上再跑一次,這次必須棒棒噠。


image

咋樣,第一次上手stack view484真香,so easy。

image

在沒有stack view,你必須使用間隔視圖來占位,每對按鈕之間有一個占位。要正確定位間隔視圖,您必須向所有間隔視圖添加等寬約束以及許多附加約束。
它看起來會像下面這樣。為了在屏幕截圖中可見,間隔視圖被賦予了淺灰色背景:

image

如果只是偶爾這么做一次其實還好,如果做多了,小伙子身體可吃不消啊。特別是動態添加視圖這種。比如隱藏例子中的WEATHER這種。

現在我們只是簡單的設置間距值,如果你想在某個視圖中設置特別的間距,在iOS11里提供的新的Api: setCustomSpacing:afterView

沙場老司機

經過上面的演練,下面我們就能很輕松的把SpotInfoViewController里需要的結構轉換成stack view

評級部分

選中評級RATING部分的兩個控件。

image

接著還是同樣的點擊Stack按鈕將其嵌入到stack view里。

image

這次我們只需要添加三個約束即可:

Top: 20, Leading: 0, Bottom: 20
image

spacing值這是為8

image

你可能會看到一個錯位的視圖警告,如下圖所示,其中星星標簽已經超出了視圖的范圍(這種警告可能因為某些beta版本的原因才會出現,沒出現那就恭喜你繼續):


image

如果確定自己的約束是正確的,因為Xcode某些版本的bug引起的視圖錯位,直接用右下角的Refresh Layout按鈕矯正即可。

image

現在看起來就正常了。


image

還原視圖

如果發現有的時候手殘,或者是處于實驗性目的,多上了stack view,直接選中需要撤銷的控件,按住Option,左擊StackUnembed即可撤銷。

image

或者選中控件從菜單Editor \ Unembed撤銷也行。

試試第一個垂直方式

現在我們來創建垂直方式的堆棧視圖,選中WHY VISIT<whyVisitLabel>兩個文本控件。

image

Xcode會根據控件的分布排列來確實方向,就比如現在這樣,直接生成的就是垂直分布的堆棧視圖。


image

現在我們來確定約束,只需要把這部分的上左右約束都設置為0,只不過底部約束的對象是相對WEATHER控件,間距是20,一看即懂。

image

設置完后默認對齊是左對齊,如下圖所示:


image

對齊屬性

對齊屬性確定堆棧視圖如何垂直于其軸布局。對于垂直堆棧視圖,可以設置的對齊方式為:

  • 填充·Fill
  • 靠左對齊·Leading
  • 居中對齊·Center
  • 靠右對齊·Trailing

水平堆棧視圖對齊屬性的值略有不同:


image

水平布局的對齊方式分別為:

  • 頂部對齊·Top
  • 靠左對齊·Leading
  • 底部對齊·Bottom
  • 靠右對齊·Trailing
  • 第一元素基準線·FirstBaseline:意思是以第一個UI元素的的內容基準線為準,后面元素內容以此基準線對齊。
  • 最后元素基準線·LastBaseline:意思是以最后一個UI元素的的內容基準線為準,前面元素內容以此基準線對齊。

大家闊以選中剛才設置好的垂直堆棧視圖,調整下對齊屬性看下變化就淺顯易懂了。
填充·Fill:

image

靠左對齊·Leading:
image

居中對齊·Center:
image

靠右對齊·Trailing:
image

跑起來看一下布局穩當冇問題。

轉換“what to see”部分

這部分轉換和前面一節沒啥區別,直接操作關鍵點即可。

  • 首先選中WHAT TO SEE<whatToSeeLabel>兩個文本元素。
  • 其次壓入堆棧視圖。
  • 對齊方式選中填充·Fill。
  • 最后設置好如下約束即可。
Top: 20, Leading: 0, Trailing: 0, Bottom: 20

設置完以后大概就長這樣:


image

轉換“weather”部分

由于需要隱藏天氣,所以這部分是比較難搞的。

我們先把這部分的所有元素都壓入堆棧視圖。先選中WEATHER文本欄和Hide按鈕,壓入水平堆棧視圖,然后在按住Command選中下面的<weatherInfoLabel>文本欄部分,將水平堆棧視圖和這部分一起壓入垂直堆棧視圖。

image

其實可以發現,水平的堆棧視圖被Hide按鈕給頂開了,如果覺得不喜歡,闊以選中底部對齊的方式來看看效果。

image

也還是不太滿意,那根據我的經驗,要不就是修改水平堆棧視圖內UI元素的約束優先級,要不就是把Hide按鈕給拆出去,單獨處理約束。為了容易理解我們還是單獨處理。

自行撤回到天氣部分的原始轉態。選中WEATHER<weatherInfoLabel>兩個文本欄部分:

image

將其壓入垂直堆棧視圖:


image

然后設置好如下約束:

Top: 20, Leading: 0, Trailing: 0, Bottom: 20

對齊方式為填充:


image

因為Hide按鈕被拆出去,所以我們需要對其添加約束以保證按鈕位置的正確性。因為需要設置約束的相對控件為WEATHER文本欄,所以我們還得再改造改造。
直接在控件列表欄選中WEATHER或使用組合拳Control-Shift-click

image

壓入堆棧視圖:


image

對齊方式·Alignment選中為靠左對齊·Leading排版·Axis確定為垂直·Vertical排版:

image

運行看看是啥情況:


image

我頂你個肺,啥情況,按鈕跑偏了?實際上是我們拆出Hide按鈕之后忘記給設置約束。
按住Control選中Hide按鈕拖動指向到WEATHER文本欄添加約束:

image

只要完成如下兩條約束,間距和縱向位置的約束即可:


image

再重新跑一下,這回就必須妥當了。


image

堆棧化

在左側控件列表里選中我們之前所有做個堆棧化的控件:


image

全部壓入堆棧:


image

然后給最外層堆棧視圖添加約束,確保勾選上Constrain to margins,四邊邊距約束為0即可。
然后間隙·Spacing設置為20對齊方式·Alignment設置為填充·Fill

image

再次運行還是會有剛才那個屌問題。


image

只要記住如果有外設相對控件的約束,只要堆棧化,約束就會被干掉,只能再次添加了:


image

完善層級

最后我們需要把what to see的部分挪動到weather部分上面去,最好的方式是在左側控件列表里拖動:

image

如果在Storyboard里直接去拖動,很可能會因為選錯控件元素而導致打亂結構。

根據屏幕大小配置間隙

由于在橫屏的情況下,垂直方向上的空間比較珍貴,所以針對橫屏,把間隙改為10比較穩妥。
找到間隙·Spacing的左側的+號按鈕:

image

選擇Any Width > Compact Height,然后添加·Add Variation

image

wAny hC欄添加值為10即可:

image

最后運行一下看看,包括橫屏(?←)也看看,484看起來比較舒爽了。


image

動畫

當前的隱藏和呈現動畫效果看起來有木有點生硬,感覺怪怪的。我們還是添加點動畫讓細節看起來比較絲滑為妙。
打開SpotInfoViewController.swift,定位到updateWeatherInfoViews(hideWeatherInfo:animated:)方法,現在看起來長這樣:

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
}

這樣跑一下看,隱藏起來就不會辣么尬了。。。

寫在最后

一切照舊,懶癌患者或者閱讀理解不過關的胖友闊以直接下載最終代碼

有關UIStackView的使用就到這里,關于使用UIStackView做高級動畫是稍稍有點復雜的,以后有空還是需要專門開一篇來做介紹。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網絡請求組件 FMDB本地數據庫組件 SD...
    陽明AGI閱讀 16,003評論 3 119
  • 今天陽光正好 我想說 你的闊腿褲在風中 顯得有些臃腫 但你的好心情我是知道的 今天你有點憤世嫉俗 牢騷發了一路 但...
    喜寧123閱讀 265評論 2 4
  • 人的一生當中,最有意義的事情就是工作,如果說,與同事相處是一種緣分,那么與顧客、生意伙伴見面就是一種樂趣。 ...
    3e1094b2ef7b閱讀 1,234評論 1 1
  • 2.17.4.10 星期一 累計71 一、目標: 本期目標實現財富收入50萬元。通過我的目標實現,希望能幫助更...
    鵲曾閱讀 256評論 0 1
  • 今年入職了某廠,金三銀四嘛,流動大,入職后部門還在招人,能看到后續其他人的面試反饋郵件,我進來當時的面試體驗,感覺...
    lxfeng閱讀 341評論 0 1