[TOC]
以下配方顯示了如何使用堆棧視圖來創建日益復雜的布局。 堆棧視圖是快速輕松設計用戶界面的強大工具。 他們的屬性允許精確控制他們如何布置他們排列的視圖。 可以使用其他自定義約束來增加這些設置; 然而,這增加了布局的復雜性。
要查看這些配方的源代碼,請參閱 [Auto Layout Cookbook](https://developer.apple.com/sample-code/xcode/downloads/Auto-Layout-Cookbook.zip) 項目。
## 簡單的堆棧視圖
此配方使用單個垂直堆棧視圖來布置文本標簽,圖像視圖和按鈕。

### 視圖和約束
在 Interface Builder 中,首先拖出一個垂直堆棧視圖,然后添加文本標簽,圖像視圖和編輯按鈕。 然后如圖所示設置約束條件。

1. Stack View.Leading = Superview.LeadingMargin
2. Stack View.Trailing = Superview.TrailingMargin
3. Stack View.Top = Top Layout Guide.Bottom + Standard
4. Bottom Layout Guide.Top = Stack View.Bottom + Standard
### 屬性
在“Attributes inspector”檢查器中,設置以下堆棧視圖屬性:
| Stack |Axis| Alignment |Distribution | Spacing |
| --- | --- | --- | --- | --- |
| Stack View| Vertical | Fill | Fill | 8 |
接下來,在 ImageView 上設置以下屬性:
| View | Attribute | Value |
| --- | --- | --- |
| Image View| Image | (an image of flowers) |
| Image View | Mode | Aspect Fit |
最后,在“ Size inspector”查器中,設置圖像視圖的內容擁抱和抗壓縮(CHCR)優先級。
| Name | Horizontal hugging | Vertical hugging | Horizontal resistance | Vertical resistance |
| --- | --- | --- | --- | --- |
| ImageView | 250 |249 | 750 | 749 |
### 討論
必須將堆棧視圖固定到父視圖,否則,堆棧視圖將管理整個布局,而不會有任何其他顯式約束。
在這個配方中,堆棧視圖填充其超級視圖,并有一個小的標準邊界。 排列的視圖被調整大小以填充堆棧視圖的邊界。 橫向上,每個視圖都被拉伸以匹配堆棧視圖的寬度。 垂直方向上,視圖基于其CHCR優先級而延伸。 圖像視圖應始終縮小、拉伸以填充可用空間。 因此,其垂直內容擁抱和壓縮阻力優先級必須低于文本標簽和按鈕的默認優先級。
最后,將圖像視圖的模式設置為` Aspect Fit`。 此設置強制圖像視圖調整圖像大小,使其適合圖像視圖的邊界,同時保持圖像的寬高比。 這可以讓堆棧視圖任意調整圖像視圖的大小而不會使圖像失真。
有關固定視圖以填充其父視圖的更多信息,請參閱 [Attributes](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithSimpleConstraints.html#//apple_ref/doc/uid/TP40010853-CH12-SW5) 和 [Adaptive Single View](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithSimpleConstraints.html#//apple_ref/doc/uid/TP40010853-CH12-SW4)。
## 嵌套堆棧視圖
這個配方顯示了從多層嵌套堆棧視圖構建的復雜布局。 然而,在這個例子中,堆棧視圖不能單獨創建想要的行為。 相反,需要額外的約束來進一步改進布局。

在構建視圖層次結構后,在下一節“視圖和約束”中展示添加約束。
### 視圖和約束
處理嵌套堆棧視圖時,最容易的順序是從內到外工作。 首先在 Interface Builder 中布置名稱行。 將標簽和文本字段放置在正確的相對位置,選擇它們,然后單擊 Editor > Embed In > Stack View 菜單項。 這會為該行創建一個水平堆棧視圖。
接下來,水平放置這些行,選擇它們,然后再次單擊 Editor > Embed In > Stack View 菜單項。 這會創建一個水平的行堆棧。 繼續構建如圖所示的界面。

1. Root Stack View.Leading = Superview.LeadingMargin
2. Root Stack View.Trailing = Superview.TrailingMargin
3. Root Stack View.Top = Top Layout Guide.Bottom + 20.0
4. Bottom Layout Guide.Top = Root Stack View.Bottom + 20.0
5. Image View.Height = Image View.Width
6. First Name Text Field.Width = Middle Name Text Field.Width
7. First Name Text Field.Width = Last Name Text Field.Width
### 屬性
每個堆棧都有自己的一組屬性。 這些定義了堆棧如何布置其內容。 在屬性檢查器中,設置以下屬性:
| Stack | Axis | Alignment |Distribution | Spacing |
| --- | --- | --- | --- | --- |
| First Name | Horizontal |First Baseline | Fill | 8 |
| Middle Name | Horizontal | First Baseline |Fill |8 |
| Last Name | Horizontal | First Baseline | Fill | 8 |
| Name Rows |Vertical |Fill | Fill | 8 |
| Upper | Horizontal | Fill | Fill | 8 |
| Button| Horizontal | First Baseline | Fill Equally | 8 |
| Root | Vertical | Fill | Fill | 8 |
此外,使文本視圖呈淺灰色背景色。 這使得更容易看到文本視圖在方向更改時如何調整大小。
| View |Attribute | Value |
| --- | --- | --- |
| Text View | Background | Light Gray Color |
最后,CHCR優先級定義哪些視圖應該延伸以填充可用空間。 在 "Size inspector" 檢查器中,設置以下CHCR優先級:
| Name | Horizontal hugging | Vertical hugging | Horizontal resistance | Vertical resistance |
| --- | --- | --- | --- | --- |
| Image View | 250 | 250 | 48 | 48 |
| Text View | 250 |249 | 250 |250 |
| First, Middle, and Last Name Labels | 251 | 251 | 750 | 750 |
| First, Middle, and Last Name Text Fields | 48 | 250 |749 |750 |
### 討論
在這個配方中,堆棧視圖一起工作來管理大部分布局。 但是,他們無法 - 自己 - 創造所有想要的行為。 例如,當圖像視圖被調整大小時,圖像應該保持其縱橫比。 不幸的是,簡單堆棧視圖中使用的技術在這里不起作用。 布局需要貼近圖像的尾部和底部邊緣,并且使用“寬高比擬合”模式會為其中一個維度添加額外的空白區域。 幸運的是,在這個例子中,圖像的高寬比總是正方形的,所以你可以讓圖像完全填充圖像視圖的邊界,并將圖像視圖限制為1:1的高寬比。
> 注意
> 在 Interface Builder 中,寬高比約束僅僅是視圖高度和寬度之間的約束。 Interface Builder 還可以通過多種方式顯示約束的乘數。 通常,對于寬高比限制,它將它們顯示為比率。 因此,View.Width = View.Height 約束可能會顯示為 1:1 寬高比。
此外,所有文本字段應該是相同的寬度。 不幸的是,它們都在單獨的堆棧視圖中,所以堆棧無法管理它。 相反,您必須明確添加相等寬度的約束。
就像簡單的堆棧視圖一樣,您還必須修改一些CHCR優先級。 這些定義了視圖如何隨著超類的邊界改變而縮小和增長。
垂直方向上,您希望文本視圖展開以填充上層堆棧和按鈕堆棧之間的空間。 因此,文本視圖的垂直內容擁抱必須低于任何其他垂直內容擁抱優先權。
橫向上,標簽應顯示為內在內容大小,而文本字段調整大小以填充任何額外空間。 默認的CHCR優先順序適用于標簽。 界面生成器已經在251設置擁抱內容,使其高于文本字段; 但是,您仍然需要降低文本字段的水平內容擁抱和水平壓縮抵抗力。
圖像視圖應該縮小,以便與包含名稱行的堆棧具有相同的高度。 但是,堆棧視圖只是松散地擁抱他們的內容。 這意味著圖像視圖的垂直壓縮阻力必須非常低,因此圖像視圖會縮小而不是展開堆棧視圖。 此外,圖像視圖的高寬比約束使布局復雜化,因為它允許垂直和水平約束進行交互。 這意味著文本字段的橫向內容擁抱也必須非常低,否則會阻止圖像視圖縮小。 在這兩種情況下,請將優先級設置為48或更低。
## 動態堆棧視圖
此配方演示了在運行時動態添加和刪除堆棧中的項目。 對堆棧的所有更改都是動畫形式的。 此外,堆棧視圖放置在滾動視圖中,如果它太長而無法放在屏幕上,則可以滾動列表。

> 注意
> 此配方僅用于演示動態使用堆棧視圖以及在滾動視圖內使用堆棧視圖。 在現實世界的應用程序中,應該使用 UITableView 類來實現此配方的行為。 一般來說,您不應該使用動態堆棧視圖來簡單地實現基于劃痕的表視圖克隆。 相反,使用它們來創建動態用戶界面,您無法使用任何其他技術輕松構建動態用戶界面。
### 視圖和約束
最初的用戶界面非常簡單。 在你的場景中放置一個滾動視圖,并調整它的大小,使其充滿場景。 然后,在滾動視圖中放置一個堆棧視圖,并將添加項按鈕放入堆棧視圖中。 只要一切就緒,請設置以下約束條件:

1. Scroll View.Leading = Superview.LeadingMargin
2. Scroll View.Trailing = Superview.TrailingMargin
3. Scroll View.Top = Superview.TopMargin
4. Bottom Layout Guide.Top = Scroll View.Bottom + 20.0
5. Stack View.Leading = Scroll View.Leading
6. Stack View.Trailing = Scroll View.Trailing
7. Stack View.Top = Scroll View.Top
8. Stack View.Bottom = Scroll View.Bottom
9. Stack View.Width = Scroll View.Width
### 屬性
在“Attributes inspector”檢查器中,設置堆棧視圖以下屬性:
| Stack | Axis | Alignment | Distribution | Spacing |
| --- | --- | --- | --- | --- |
| Stack View | Vertical | Fill | Equal Spacing | 0 |
### 代碼
這個配方需要一些代碼來添加項目并將其從堆棧視圖中移除。 為您的場景創建自定義視圖控制器,同時為滾動視圖和堆棧視圖提供插口。
~~~
class DynamicStackViewController: UIViewController {
@IBOutlet weak private var scrollView: UIScrollView!
@IBOutlet weak private var stackView: UIStackView!
// Method implementations will go here...
}
~~~
接下來,重寫 viewDidLoad 方法來設置滾動視圖的初始位置。 希望滾動視圖的內容在狀態欄下方開始。
~~~
override func viewDidLoad() {
super.viewDidLoad()
// setup scrollview
let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
scrollView.contentInset = insets
scrollView.scrollIndicatorInsets = insets
}
~~~
現在,為添加項目按鈕添加一個操作方法。
~~~
// MARK: Action Methods
@IBAction func addEntry(sender: AnyObject) {
let stack = stackView
let index = stack.arrangedSubviews.count - 1
let addView = stack.arrangedSubviews[index]
let scroll = scrollView
let offset = CGPoint(x: scroll.contentOffset.x,
y: scroll.contentOffset.y + addView.frame.size.height)
let newView = createEntry()
newView.hidden = true
stack.insertArrangedSubview(newView, atIndex: index)
UIView.animateWithDuration(0.25) { () -> Void in
newView.hidden = false
scroll.contentOffset = offset
}
}
~~~
此方法計算滾動視圖的新偏移量,然后創建新的條目視圖。 輸入視圖是隱藏的,并添加到堆棧中。 隱藏的視圖不會影響堆棧的外觀或布局 - 所以堆棧的外觀保持不變。 然后,在動畫塊中,顯示視圖并更新滾動偏移,為視圖的外觀添加動畫。
添加一個類似的方法來刪除條目; 但是,與 `addEntry` 方法不同,此方法未鏈接到 Interface Builder 中的任何控件。 相反,應用程序會在創建視圖時以編程方式將每個條目視圖鏈接到此方法。
~~~
func deleteStackView(sender: UIButton) {
if let view = sender.superview {
UIView.animateWithDuration(0.25, animations: { () -> Void in
view.hidden = true
}, completion: { (success) -> Void in
view.removeFromSuperview()
})
}
}
~~~
該方法將視圖隱藏在動畫塊中。 動畫完成后,它將從視圖層次結構中刪除視圖。 這會自動從堆棧的排列視圖列表中刪除視圖。
盡管入口視圖可以是任何視圖,但此示例使用包含日期標簽,包含隨機十六進制字符串的標簽和刪除按鈕的堆棧視圖。
~~~
// MARK: - Private Methods
private func createEntry() -> UIView {
let date = NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .ShortStyle, timeStyle: .NoStyle)
let number = "\(randomHexQuad())-\(randomHexQuad())-\(randomHexQuad())-\(randomHexQuad())"
let stack = UIStackView()
stack.axis = .Horizontal
stack.alignment = .FirstBaseline
stack.distribution = .Fill
stack.spacing = 8
let dateLabel = UILabel()
dateLabel.text = date
dateLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
let numberLabel = UILabel()
numberLabel.text = number
numberLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
let deleteButton = UIButton(type: .RoundedRect)
deleteButton.setTitle("Delete", forState: .Normal)
deleteButton.addTarget(self, action: "deleteStackView:", forControlEvents: .TouchUpInside)
stack.addArrangedSubview(dateLabel)
stack.addArrangedSubview(numberLabel)
stack.addArrangedSubview(deleteButton)
return stack
}
private func randomHexQuad() -> String {
return NSString(format: "%X%X%X%X",
arc4random() % 16,
arc4random() % 16,
arc4random() % 16,
arc4random() % 16
) as String
}
}
~~~
### 討論
* 如此配方演示,視圖可以在運行時添加或從堆棧視圖中刪除。 堆棧的布局會自動調整以補償其排列視圖陣列的更改。 但是,有一些重要的值得記住的地方:
* 隱藏的視圖仍然位于堆棧的排列視圖陣列內。 但是,它們不會顯示,也不會影響其他排列視圖的布局。
* 將視圖添加到堆棧的排列視圖數組中會自動將其添加到視圖層次結構中。
* 從堆棧的排列視圖陣列中移除視圖不會自動從視圖層次中移除視圖; 但是,從視圖層次結構中刪除視圖會將其從排列的視圖數組中移除。
* 在 iOS 中,視圖的隱藏屬性通常不是可以動畫的。 然而,只要將這些屬性放入堆棧排列的視圖數組中,該屬性就會變為動畫。 實際的動畫由堆棧管理,而不是視圖。 使用隱藏屬性來為視圖添加視圖或將其從堆棧中移除。
此配方還介紹了使用自動布局和滾動視圖的想法。 這里,堆棧和滾動視圖之間的約束設置滾動視圖的內容區域的大小。 等寬限制顯式設置堆棧(以及內容大小)以水平填充滾動視圖。 垂直方向上,內容尺寸基于堆疊的配件尺寸。 隨著用戶添加更多條目,堆棧視圖變長。 只要內容太多而無法在屏幕上顯示,滾動就會自動啟用。
有關更多信息,請參閱 [Working with Scroll Views](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithScrollViews.html#//apple_ref/doc/uid/TP40010853-CH24-SW1)。
>原文地址
>[Stack Views ](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/LayoutUsingStackViews.html#//apple_ref/doc/uid/TP40010853-CH11-SW12)