第三章——視圖和視圖層級【譯】

在接下來的五章中,您將構建一個名為 WorldTrotter 的應用程序。 完成后,此應用程序將轉換華氏溫度和攝氏溫度之間的值。 在本章中,您將通過創建 WorldTrotter 的UI來了解視圖和視圖層級。 在本章結尾,您的應用程序將如圖3.1所示。

圖3.1 WorldTrotter

我們先從一些視圖和視圖層級的理論入手。

視圖基礎知識

回顧第1章,視圖是用戶可見的對象,如按鈕,文本字段和滑塊。 視圖對象組成應用程序的UI。 一個視圖:

  • UIView 或其子類的一個實例
  • 知道如何繪制自己
  • 可以處理事件,如觸摸
  • 存在于其根是應用程序的窗口的視圖的層級中

讓我們更詳細地看看 視圖層級(view hierarchy)

視圖分層

每個應用程序都有一個單一的 UIWindow 實例,作為應用程序中所有視圖的容器。 UIWindowUIView 的一個子類,所以窗口本身就是一個視圖。 該窗口在應用程序啟動時創建。 創建窗口后,可以添加其他視圖。

當視圖被添加到窗口時,它被認為是窗口的子視圖。 作為窗口子視圖的視圖也可以具有子視圖,結果是生成以窗口為根的視圖對象的層級(圖3.2)。

圖3.2 示例視圖層級及其創建的界面

一旦創建了視圖層級,它將被繪制到屏幕上。 這個過程可以分為兩個步驟:

  • 層級中的每個視圖(包括窗口)自身繪制。 它將自身渲染到其 圖層(layer),您可以將其視為位圖圖像。 (該圖層是 CALayer 的一個實例。)
  • 所有視圖的圖層在屏幕上合成在一起。

圖3.3描繪了另一個示例視圖層級和兩個繪圖步驟。

圖3.3 視圖渲染自身,然后合成在一起

對于 WorldTrotter,您將創建一個由不同視圖組成的界面。 將有四個 UILabel 實例和一個 UITextField 實例,允許用戶輸入華氏溫度。 讓我們開始吧。

創建新項目

Xcode 中,選擇 FileNewProject... (或使用鍵盤快捷鍵Command-Shift-N)。 在頂部的 iOS 部分下,選擇 Application 下的 Single View Application 模板,然后單擊 Next

Project Name 輸入 WorldTrotter 。 確保從 Language 下拉菜單中選擇了 Swift,并從 Devices 下拉列表中選擇 Universal。 還要確保 Use Core Data 沒被選中(圖3.4)。 單擊 Next,接著點擊 Create

圖3.4 配置 WorldTrotter

視圖和邊框

當您以編程方式初始化視圖時,您可以使用指定的初始化程序 init(frame :) 。 該方法使用一個參數 CGRect,它將成為視圖的 邊框(frame),即 UIView 上的一個屬性。

var frame: CGRect

視圖的 *邊框(frame)* 指定視圖的大小及其相對于其父級視圖的位置。 由于視圖的大小始終由其邊框指定,所以視圖始終是一個矩形。

CGRect 包含成員 originsizeoriginCGPoint 類型的結構,并包含兩個 CGFloat 屬性:xysizeCGSize 類型的結構體,并具有兩個 CGFloat 屬性:widthheight(圖3.5)。

圖3.5 CGRect

當應用程序啟動時,初始視圖控制器的視圖將添加到根視圖中。 該視圖控制器由 ViewController.swift 中定義的 ViewController 類表示。 我們將在第 5 章討論視圖控制器,但現在只要知道一個視圖控制器對應一個視圖就足夠了,并且與應用程序主視圖控制器相關聯的視圖會作為一個子視圖被添加到窗口下。

在創建 WorldTrotter 的視圖之前,您將以編程方式添加一些練習視圖,以查看視圖及其屬性,并了解如何創建應用程序界面。

打開 ViewController.swift 并刪除模板創建的任何方法。 您的文件應如下所示:

import UIKit

class ViewController: UIViewController {

}

(UIKit,您也在第1章中看到)是一個 框架(framework),一個框架是相關類和資源的集合,UIKit 框架定義了用戶看到的許多UI元素以及其他iOS特定的類。 這本書將使用幾個不同的框架。)

在視圖控制器的視圖加載到內存之后,它的 viewDidLoad() 方法被調用。 這種方法為您提供了自定義視圖層級的機會,因此它是添加練習視圖的好地方。

ViewController.swift 中,覆蓋 viewDidLoad()。 創建一個將成為 UIView 邊框的 CGRect。 接下來,創建一個 UIView 的實例,并將其 backgroundColor 屬性設置為藍色。 最后,將 UIView 添加為視圖控制器視圖的子視圖,使其成為視圖層級的一部分。 (其中大部分看起來不會很熟悉,沒關系,輸入代碼后我們會解釋一下。)

class ViewController: UIViewController {

??override func viewDidLoad() {
????super.viewDidLoad()

????let firstFrame = CGRect(x: 160, y: 240, width: 100, height: 150)
????let firstView = UIView(frame: firstFrame)
????firstView.backgroundColor = UIColor.blue
????view.addSubview(firstView)
??}
}

要創建 CGRect,您可以使用它的構造器并傳入 origin.xorigin.ysize.widthsize.height 的值。

您將使用 UIColor 類屬性 Blue 來設置 backgroundColor。 這是一個初始化配置為 blueUIColor 實例。 有很多常用的 UIColor 類屬性,如 greenblackclear

構建并運行應用程序(Command-R)。 您將看到一個藍色矩形,它是 UIView 的實例。 因為 UIView 的邊框的起點是(160,240),所以矩形的左上角為相對于其父視圖 向右偏移 160 點 向下偏移 240 點。 根據 size 屬性,視圖將向右延伸100點,從 origin 向下延伸150點(圖3.6)。

圖3.6具有一個UIView的WorldTrotter

請注意,這些值是點而不是像素。 如果值是以像素為單位,則它們在不同分辨率的顯示(如 Retina versus 與 non-Retina膜 )上不一致。 點是度量的相對單位; 它是不同數量的像素,這取決于顯示器中有多少個像素。 大小,位置,線條和曲線總是以點表示,以便適配不同分辯率的設備。

圖3.7表示您創建的視圖層級。

圖3.7當前視圖層級

UIView 的每個實例都有一個 superview 屬性。 當您將視圖添加為另一個視圖的子視圖時,反向關系將自動建立。 在當前情況下,UIView
superviewUIWindow

我們來試驗視圖層級。 首先,在 ViewController.swift 中,使用不同的 frame 和背景顏色創建另一個 UIView 實例。

override func viewDidLoad() {
??super.viewDidLoad()

??let firstFrame = CGRect(x: 160, y: 240, width: 100, height: 150)
??let firstView = UIView(frame: firstFrame)
??firstView.backgroundColor = UIColor.blue
??view.addSubview(firstView)

??let secondFrame = CGRect(x: 20, y: 30, width: 50, height: 50)
??let secondView = UIView(frame: secondFrame)
??secondView.backgroundColor = UIColor.green
??view.addSubview(secondView)
}

構建并再次運行。 除了藍色矩形之外,您將在窗口的左上角附近看到一個綠色的正方形。 圖3.8顯示了更新后的視圖層級。

圖3.8 更新后的視圖層級,兩個子視圖作為兄弟

現在,您將調整視圖層次結構,以使 UIView 的一個實例是另一個 UIView 的子視圖,而不是視圖控制器的視圖。 在 ViewController.swift 中,添加 secondView 作為 firstView 的子視圖。

...
let secondView = UIView(frame: secondFrame)
secondView.backgroundColor = UIColor.green
view.addSubview(secondView)
firstView.addSubview(secondView)

您的視圖層級現在有四層深,如圖3.9所示。

圖3.9 一個UIView作為另一個的子視圖

構建并運行應用程序。 請注意,secondView 在屏幕上的位置已更改(圖3.10)。 視圖的 邊框(frame) 是相對與其父級視圖的,所以 secondView 的左上角(20,30)是以 firstView左上角為參照的。

圖3.10具有新層級的WorldTrotter

(如果 綠色的 UIView 實例看起來比以前更小,這只是一個幻覺,其實它的大小沒有改變。)

現在您已經了解了視圖和視圖層級的基礎知識,可以開始在 WorldTrotter 的界面上工作了。 但不是以編程方式構建界面,您將如第一章一樣去使用 Interface Builder 來直觀地布局界面。

在 ViewController.swift 中,首先刪除練習代碼。

override func viewDidLoad() {
??super.viewDidLoad()

??let firstFrame = CGRect(x: 160, y: 240, width: 100, height: 150)
??let firstView = UIView(frame: firstFrame)
??firstView.backgroundColor = UIColor.blue
??view.addSubview(firstView)

??let secondFrame = CGRect(x: 20, y: 30, width: 50, height: 50)let secondView = UIView(frame: secondFrame)
??secondView.backgroundColor = UIColor.green
??firstView.addSubview(secondView)
}

現在讓我們添加一些視圖到界面并設置它們的邊框。

打開 Main.storyboard。 在畫布的底部,確保 View as 按鈕配置為顯示 iPhone 7 設備。

從對象庫中,拖放五個 UILabel 的實例到畫布上。 將其文本設置為圖3.11所示,將它們在界面的上半部分垂直放置,并將它們水平放置。

圖3.11添加標簽到界面

選擇頂部標簽,以便您可以在 Interface Builder 中看到它的邊框。 打開其 大小檢查器(size inspector) ——公用程序區域中的第五個選項卡。 (實用程序選項卡的鍵盤快捷鍵為 Command-Option 加上選項卡號,尺寸檢查器是第五個選項卡,因此鍵盤快捷鍵為 Command-Option-5。)win+Alt+5

View 部分下,找到 Frame Rectangle。 (如果沒有看到,可能需要從 show 彈出菜單中選擇它)。顯示的值是視圖的 邊框(frame),它們決定了屏幕上的視圖位置(圖3.12)。

圖3.12查看邊框值

在 iPhone 7 模擬器上構建并運行應用程序。 模擬器上的界面與 Interface Builder 中布置的界面看起來是一樣的。

自定義標簽

通過自定義視圖屬性,讓界面看起來更漂亮。

Main.storyboard 中,選擇背景視圖。 打開屬性檢查器并給應用程序一個新的背景顏色:查找并單擊 Background 下拉列表并單擊 Other...。選擇第二個選項卡(Color Sliders 選項卡),并從下拉列表中選擇 RGB Sliders。 在底部附近的 Hex Color # 框中輸入 F5F4F1 (圖3.13)。 這將給背景一個溫暖的灰色。

圖3.13更改背景顏色

您可以同時自定義所選視圖的常用屬性。 您將使用它來使許多標簽擁有更大的字體大小以及燒焦的橙色文本顏色。

通過在文檔大綱中按住Win鍵單擊它們來選擇頂部的兩個和底部兩個標簽。 確保屬性檢查器已打開,并更新文本顏色:在 Label 部分下,找到 Color 并打開彈出菜單。 再次選擇 Color Sliders 選項卡,在 Hex Color # 輸入 E15829

現在我們來更新字體。 選擇 212100 這兩個標簽。 在屬性檢查器的 Label 部分下,查找 Font,然后單擊當前字體旁邊的文本圖標。 在出現的彈出框中,Size 改為 70(圖3.14)。 選擇剩余的三個標簽。 打開他們的 Font 彈出窗口并修改 Size 為 36。

圖3.14自定義標簽的字體

現在字體大小較大,文本不再符合標簽的范圍。 您可以手動調整標簽大小,但有一種更簡單的方法。

選擇畫布上的頂部標簽。 從 XcodeEditor 菜單中,選擇 Size to Fit Content(Command- =)。 這將調整標簽的大小以完全適合其文本內容。 對其他四個標簽重復此過程。 (您可以選擇所有四個標簽來一次性調整它們的大小。)現在移動標簽,使其再次垂直對齊并水平居中(圖3.15)。

圖3.15 更新標簽邊框

在 iPhone 7 模擬器上構建并運行應用程序。 然后在 iPhone 7 Plus 模擬器上構建并運行應用程序。 請注意,標簽不再居中——而是在偏左的位置。

你剛剛看到絕對邊框的兩個主要問題。 首先,當內容發生變化時(如改變字體大小),邊框不會自動更新。 第二,視圖在不同尺寸的屏幕上看起來并不是很好。

一般來說,您不應該在您的視圖中使用絕對邊框。相反,您應該使用自動布局,根據您為每個視圖指定的約束來靈活地計算邊框。例如,對于 WorldTrotter 來說,你真正想要的是讓標簽保持與屏幕頂端相同的距離,并保持水平居中在他們的父視圖中。如果標簽的字體或文本發生變化,它們也應該進行更新。這是在下一節中您將完成的任務。

自動布局系統

在你可以修改標簽讓它們靈活地布局之前,你需要學習一些關于自動布局系統的理論。正如您在第1章中看到的,絕對坐標使您的布局變得很脆弱,因為他們假定您提前知道屏幕的大小。

使用自動布局,您可以用相對的方式描述視圖的布局,以便在運行時確定它們的邊框,因此邊框可以按照正在運行的設備的屏幕大小來調整應用程序的布局。

對齊矩形和布局屬性

自動布局系統基于對齊矩形。 該矩形由幾個布局屬性定義(圖3.16)。

圖3.16定義視圖的對齊矩形的布局屬性

Width/Height

這些值確定對齊矩形的大小。

Top/Bottom
Left/Right

這些值確定對齊矩形的給定邊和層級中另一視圖的對齊矩形之間的間距。

CenterX
CenterY

這些值確定對齊矩形的中心點。

FirstBaseline
LastBaseline

這些值與大多數(而不是全部)視圖的底部屬性相同。 例如,UITextField 將其基線定義為其顯示的文本的底部,而不是對齊矩形的底部。 這使得“下降”(在基線下方的“g”和“p”字母的部分)不被文本字段下方的視圖遮蔽。 對于多行文本標簽和文本視圖,第一個和最后一個基準指的是文本的第一行和最后一行。 在所有其他情況下,第一個和最后一個基線是相同的。

Leading
Trailing

這些值是語言特定的屬性。 如果設備設置為從左到右讀取的語言(例如,英文),則 前導(Leading) 屬性與左側屬性相同, 尾隨(Trailing) 屬性與右側屬性相同。 如果語言從左到右(例如阿拉伯語)讀取,則前導屬性在右側,尾部屬性在左側。 界面生成器(Interface Builder) 自動優先于左和右的前導和尾隨,一般來說,您也應該這樣做。

默認情況下,每個視圖都有一個對齊矩形,每個視圖層次結構都使用自動布局。

對齊矩形與邊框非常相似。 事實上,這兩個矩形通常是一樣的。 而邊框包含整個視圖,而對齊矩形僅包含您希望用于對齊目的的內容。 圖3.17顯示了邊框和對齊矩形不同的示例。

圖3.17邊框 VS 對齊矩形

您不能直接定義視圖的對齊矩形。 你沒有足夠的信息(如屏幕尺寸)來做到這一點。 相反你應該提供一組約束。 總而言之,這些約束使系統能夠為視圖層次結構中的每個視圖確定布局屬性,從而確定對齊矩形。

約束

約束(constraint) 定義視圖層級中的特定關系,可用于確定一個或多個視圖的布局屬性。 例如,您可以添加一個約束,如 “這兩個視圖之間的垂直間距應始終為8點”,或者 “這些視圖必須始終具有相同的寬度”。也可以使用約束來給視圖一個固定的 大小,如 “這個視圖的高度應該總是44點”。

您不需要對每個布局屬性都添加約束。 一些值可能直接來自約束; 其他將由相關布局屬性的值計算。 例如,如果視圖的約束設置其左邊緣和其寬度,則右邊緣已經確定(左邊緣+寬度=右邊緣)。 一般來說,每個維度至少需要兩個約束(水平和垂直)。

如果在考慮所有約束之后,布局屬性仍然存在歧義或缺失值,那么自動布局會出現錯誤和警告,并且您的界面在所有設備上看起來都不會像您所期望的那樣。 調試這些問題很重要,您將在本章后面的一些練習中進行實踐。

你該如何去設置約束呢? 讓我們來看看如何使用您在畫布上布置的標簽。

首先,描述您想要的視圖看起來與屏幕大小無關。例如,你可能會說你想要上面的標簽是:

  • 距離屏幕頂部 8 點
  • 在它的父視圖中水平居中
  • 與文本一樣寬和高

要將此描述轉換為 Interface Builder 中的約束,它將有助于了解如何查找視圖的 最近鄰居(nearest neighbor)。 nearest neighbor 是指定方向上最接近的同級視圖(圖3.18)。

圖3.18最近鄰居

如果視圖在指定方向上沒有任何兄弟姐妹,則最近的鄰居是其父級視圖,也是其容器。

現在您可以明確標簽的約束:

  1. 標簽的頂部邊緣應距離其最近的鄰居(這是其容器 - ViewController 的視圖)8點。
  2. 標簽的中心應與其父視圖中心相同。
  3. 標簽的寬度應等于其字體大小呈現的文本的寬度。
  4. 標簽的高度應等于其字體大小呈現的文本的高度。

如果你考慮第一個和第四個約束,你可以看到沒有必要明確地約束標簽的底邊。 它將根據標簽上邊緣和標簽高度的約束來確定。 類似地,第二和第三約束一起確定標簽的右邊緣和左邊緣。

現在您知道如何去設計頂部標簽,您將添加這些約束。 可以使用 Interface Builder 或代碼的方式添加約束。 Apple 建議您盡可能使用 Interface Builder 添加約束,這就是您將在此處執行的操作。 但是,如果您的視圖是以編程方式創建和配置的,則可以在代碼中添加約束。 在第六章中,你會練習這種方法。

在 Interface Builder 中添加約束

讓我們開始約束這個頂部的標簽。

選擇畫布上的頂部標簽。 在畫布的右下角找到自動布局約束菜單(圖3.19)。

圖3.19自動布局約束菜單

單擊


圖標(左側第四個)以顯示 Add New Constraints 菜單。 此菜單顯示標簽的當前大小和位置。

Add New Constraints 菜單的頂部有四個值,它們描述標簽與畫布上最近鄰居的當前間距。 對于此標簽,您只對 top 值感興趣。

要將此值變為約束,請單擊頂部的紅色支柱,將值與中間的正方形分開。 支柱將成為一條紅色實線。

在菜單中間找到標簽的 WidthHeightWidthHeight 旁邊的值表示當前畫布值。 要將標簽的寬度和高度限制為當前畫布值,請選中 WidthHeight 旁邊的框。點擊菜單底部的按鈕 Add 3 Constraints

此時,您尚未指定足夠的約束來完全確定對齊矩形。 標簽周圍的紅色輪廓表示其對齊矩形未完全定義,而 Interface Builder 將幫助找出問題所在。

注意 Interface Builder 的右上角黃色警告標志(圖3.20)。 點擊此圖標顯示問題:
Horizontal position is ambiguous for "212".
“212” 的水平位置不明確

圖3.20 水平方向不確定

您添加了兩個垂直約束(頂邊約束和高度約束),但是您只添加了一個水平約束(寬度約束)。 只有一個約束使得標簽的水平位置不明確。 您將通過在標簽和其父級視圖之間添加中心對齊約束來解決此問題。

選中頂部標簽后,單擊


圖標(左側的第三個圖標)顯示 對齊(Align) 菜單。 如果您選擇了多個視圖,則此菜單將允許您在視圖中對齊屬性。 因為您只選擇了一個標簽,所以您唯一的選擇是在其容器內對齊視圖。

Align 菜單中,檢查 Horizontally in Container(現在請勿單擊 Add 1 Constraint)。 添加此約束后,將有足夠的約束來完全確定對齊矩形。 要確保標簽的邊框與指定的約束匹配,請從 Align 菜單中打開 Update Frames 彈出菜單,然后選擇 Items of New Constraints。 這將重新定位標簽以匹配已添加的約束。 現在單擊 *Add 1 Constraint * 以添加居中約束并重新定位標簽。

標簽的約束都是藍色的,因為標簽的對齊矩形已經完全指定。 此外,Interface Builder 右上角的警告現在已經沒了。

在 iPhone 7 模擬器和 iPhone 7 Plus 模擬器上構建和運行應用程序。 頂部標簽在兩個模擬器中都能保持居中。

內在內容大小

雖然頂部標簽的位置是靈活的,但它的大小不是。 這是因為您已經向標簽添加了明確的寬度和高度限制。 如果文字或字體改變,您將處于與之前相同的位置。 邊框的大小是絕對的,所以邊框不會隨內容修改而變化。

這是視圖的 內在內容大小(intrinsic content size) 發揮作用的地方。 您可以將內在內容大小視為視圖“想要”的大小。 對于標簽,此大小是以給定字體呈現的文本的大小。 對于圖像,就是圖像本身的大小。

視圖的內在內容大小作為隱式的寬度和高度限制。 如果不明確指定寬度的約束,則視圖將是其固有寬度。 高度也一樣。

了解這些之后,我們可以通過刪除顯式的寬度和高度約束,讓頂部標簽具有靈活的大小。

Main.storyboard 中,選擇標簽上的寬度約束。 您可以通過單擊畫布上的約束來執行此操作。 或者,在文檔大綱中,您可以單擊 212 標簽旁邊的倒三角形,然后列出標簽的約束列表(圖3.21)。

圖3.21選擇寬度約束

選擇寬度約束后,按 Delete 鍵。 對于高度約束也是一樣的。

請注意,標簽的約束仍然是藍色。 因為寬度和高度是從標簽的固有內容大小推斷出來的,所以仍然有足夠的約束來確定標簽的對齊矩形。

視圖錯位

如您所見,藍色約束表示視圖的對齊矩形是完全指定的。 橙色約束通常表示 視圖錯位(misplaced view)。 這意味著Interface Builder中視圖的邊框與
Auto Layout 所計算的邊框不同。

視圖錯位很容易解決。因為在使用自動布局時, 這是一個很常見的問題。

讓您的頂部標簽出現錯位置視圖,以便您可以看到如何解決此問題。 使用調整大小控件調整畫布上的頂部標簽大小,并在畫布的右上角查找黃色警告。 單擊此警告圖標以顯示問題:
Frame for "212" will be different at run time (Figure 3.22).
運行時“212”的邊框將不同(圖3.22)

圖3.22 視圖錯位警告

如警告所述,運行時的邊框與畫布上指定的邊框不同。 如果仔細觀察,您將看到一條橙色的虛線,表示運行時邊框是什么樣子的。

構建并運行應用程序。 請注意,盡管您在 Interface Builder 中提供了新的邊框,標簽仍然會居中。 這可能看起來很棒——畢竟這就是你想要的結果。 但是,當您繼續去構建視圖時,在 Interface Builder 中指定的與自動布局計算出來的約束之間的脫節會導致問題的發生。 我們來解決這個問題。

回到 故事板(storyboard),選擇畫布上的頂部標簽。 單擊


圖標(最左側的圖標),更新標簽的邊框,以匹配約束計算出來的邊框。

使用自動布局時,您將非常習慣更新視圖的邊框。 建議:如果您嘗試更新沒有足夠約束的視圖的邊框,您會得到意料之外的結果。 如果發生這種情況,請撤消更改并檢查約束以查看缺失的內容。

此時,頂部標簽形狀良好。 它有足夠的約束來確定其對齊矩形,并且視圖正在布置您想要的方式。

熟練使用自動布局需要很多經驗,因此在下一部分中,您將從頂部標簽中刪除約束,然后向所有標簽添加約束。

增加更多的約束

我們來看看其余的視圖的約束。 在執行此操作之前,先刪除頂部標簽的現有約束。

選擇畫布上的頂部標簽。 打開 Resolve Auto Layout Issues 菜單,然后從 Selected Views 部分中選擇 Clear
Constraints
(圖3.23)。

圖3.23 清除約束

您將通過兩個步驟將約束添加到所有視圖。 首先,您將在父級視圖中水平放置頂部標簽。 然后,您將添加約束,將每個標簽的頂部定位到其最近的鄰居,同時對齊所有標簽的中心。

選擇頂部標簽。 打開 Align 菜單,并在容器中選擇 Horizontally in Container,設置為常量 0.確保沒有選中 Update Frames; 請記住,您不會想更新沒有足夠約束的視圖的邊框,而這個約束肯定不會提供足夠的信息來計算對齊矩形。 接著點擊 Add 1 Constraint

現在選擇畫布上的所有五個標簽。 同時添加多個視圖的約束是非常方便的。 打開 Add New Constraints 菜單并進行以下選擇:

  1. 選擇頂部支柱并確保它為 8
  2. Align 菜單中選擇 Horizontal Centers
  3. Update Frames 菜單中,選擇 Items of New Constraints

您的菜單應該會像 圖3.24 一樣。 完成后,單擊 Add 9 Constraints。 這將為視圖添加約束并更新其邊框以響應自動布局更改。

圖3.24使用 Add New Constraints 菜單添加更多約束

在 iPhone 7 模擬器上構建并運行應用程序。 視圖將會居中。 接著在 iPhone 7 Plus 模擬器上構建并運行應用程序。 與本章之前不同,所有標簽都居中在較大的屏屏幕上。

自動布局是每個iOS開發人員的關鍵技術。 它可以幫助您創建靈活的布局,適用于各種設備和界面大小。 掌握它也需要很多的練習。 在使用本書時,您將獲得使用自動布局的豐富經驗。

青銅挑戰:更多的自動布局實踐

ViewController 界面中刪除所有約束,然后將其重新插入。嘗試在不看本書的情況下完成。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,247評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,520評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,362評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,805評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,541評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,896評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,887評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,062評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,608評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,356評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,555評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,077評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,769評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,175評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,489評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,289評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,516評論 2 379

推薦閱讀更多精彩內容