在接下來的五章中舰始,您將構(gòu)建一個(gè)名為 WorldTrotter 的應(yīng)用程序鸠儿。 完成后,此應(yīng)用程序?qū)⑥D(zhuǎn)換華氏溫度和攝氏溫度之間的值末捣。 在本章中,您將通過創(chuàng)建 WorldTrotter 的UI來了解視圖和視圖層級(jí)创橄。 在本章結(jié)尾箩做,您的應(yīng)用程序?qū)⑷鐖D3.1所示。
圖3.1 WorldTrotter
我們先從一些視圖和視圖層級(jí)的理論入手妥畏。
視圖基礎(chǔ)知識(shí)
回顧第1章邦邦,視圖是用戶可見的對(duì)象,如按鈕醉蚁,文本字段和滑塊燃辖。 視圖對(duì)象組成應(yīng)用程序的UI。 一個(gè)視圖:
- 是 UIView 或其子類的一個(gè)實(shí)例
- 知道如何繪制自己
- 可以處理事件网棍,如觸摸
- 存在于其根是應(yīng)用程序的窗口的視圖的層級(jí)中
讓我們更詳細(xì)地看看 視圖層級(jí)(view hierarchy)
視圖分層
每個(gè)應(yīng)用程序都有一個(gè)單一的 UIWindow 實(shí)例郭赐,作為應(yīng)用程序中所有視圖的容器。 UIWindow 是 UIView 的一個(gè)子類确沸,所以窗口本身就是一個(gè)視圖捌锭。 該窗口在應(yīng)用程序啟動(dòng)時(shí)創(chuàng)建。 創(chuàng)建窗口后罗捎,可以添加其他視圖观谦。
當(dāng)視圖被添加到窗口時(shí),它被認(rèn)為是窗口的子視圖桨菜。 作為窗口子視圖的視圖也可以具有子視圖豁状,結(jié)果是生成以窗口為根的視圖對(duì)象的層級(jí)(圖3.2)。
圖3.2 示例視圖層級(jí)及其創(chuàng)建的界面
一旦創(chuàng)建了視圖層級(jí)倒得,它將被繪制到屏幕上泻红。 這個(gè)過程可以分為兩個(gè)步驟:
- 層級(jí)中的每個(gè)視圖(包括窗口)自身繪制。 它將自身渲染到其 圖層(layer)霞掺,您可以將其視為位圖圖像谊路。 (該圖層是 CALayer 的一個(gè)實(shí)例。)
- 所有視圖的圖層在屏幕上合成在一起菩彬。
圖3.3描繪了另一個(gè)示例視圖層級(jí)和兩個(gè)繪圖步驟缠劝。
圖3.3 視圖渲染自身,然后合成在一起
對(duì)于 WorldTrotter骗灶,您將創(chuàng)建一個(gè)由不同視圖組成的界面惨恭。 將有四個(gè) UILabel 實(shí)例和一個(gè) UITextField 實(shí)例,允許用戶輸入華氏溫度耙旦。 讓我們開始吧脱羡。
創(chuàng)建新項(xiàng)目
在 Xcode 中,選擇 File → New → Project... (或使用鍵盤快捷鍵Command-Shift-N)免都。 在頂部的 iOS 部分下锉罐,選擇 Application 下的 Single View Application 模板,然后單擊 Next琴昆。
Project Name 輸入 WorldTrotter 氓鄙。 確保從 Language 下拉菜單中選擇了 Swift,并從 Devices 下拉列表中選擇 Universal业舍。 還要確保 Use Core Data 沒被選中(圖3.4)抖拦。 單擊 Next,接著點(diǎn)擊 Create舷暮。
圖3.4 配置 WorldTrotter
視圖和邊框
當(dāng)您以編程方式初始化視圖時(shí)态罪,您可以使用指定的初始化程序 init(frame :) 。 該方法使用一個(gè)參數(shù) CGRect下面,它將成為視圖的 邊框(frame)复颈,即 UIView 上的一個(gè)屬性。
var frame: CGRect
視圖的 *邊框(frame)* 指定視圖的大小及其相對(duì)于其父級(jí)視圖的位置沥割。 由于視圖的大小始終由其邊框指定耗啦,所以視圖始終是一個(gè)矩形凿菩。
CGRect 包含成員 origin 和 size。 origin 是 CGPoint 類型的結(jié)構(gòu)帜讲,并包含兩個(gè) CGFloat 屬性:x 和 y衅谷。 size 是 CGSize 類型的結(jié)構(gòu)體,并具有兩個(gè) CGFloat 屬性:width 和 height(圖3.5)似将。
圖3.5 CGRect
當(dāng)應(yīng)用程序啟動(dòng)時(shí)获黔,初始視圖控制器的視圖將添加到根視圖中。 該視圖控制器由 ViewController.swift 中定義的 ViewController 類表示在验。 我們將在第 5 章討論視圖控制器玷氏,但現(xiàn)在只要知道一個(gè)視圖控制器對(duì)應(yīng)一個(gè)視圖就足夠了,并且與應(yīng)用程序主視圖控制器相關(guān)聯(lián)的視圖會(huì)作為一個(gè)子視圖被添加到窗口下腋舌。
在創(chuàng)建 WorldTrotter 的視圖之前盏触,您將以編程方式添加一些練習(xí)視圖,以查看視圖及其屬性侦厚,并了解如何創(chuàng)建應(yīng)用程序界面耻陕。
打開 ViewController.swift 并刪除模板創(chuàng)建的任何方法。 您的文件應(yīng)如下所示:
import UIKit
class ViewController: UIViewController {
}
(UIKit刨沦,您也在第1章中看到)是一個(gè) 框架(framework)诗宣,一個(gè)框架是相關(guān)類和資源的集合,UIKit 框架定義了用戶看到的許多UI元素以及其他iOS特定的類想诅。 這本書將使用幾個(gè)不同的框架召庞。)
在視圖控制器的視圖加載到內(nèi)存之后,它的 viewDidLoad() 方法被調(diào)用来破。 這種方法為您提供了自定義視圖層級(jí)的機(jī)會(huì)篮灼,因此它是添加練習(xí)視圖的好地方。
在 ViewController.swift 中徘禁,覆蓋 viewDidLoad()诅诱。 創(chuàng)建一個(gè)將成為 UIView 邊框的 CGRect。 接下來送朱,創(chuàng)建一個(gè) UIView 的實(shí)例娘荡,并將其 backgroundColor 屬性設(shè)置為藍(lán)色。 最后驶沼,將 UIView 添加為視圖控制器視圖的子視圖炮沐,使其成為視圖層級(jí)的一部分。 (其中大部分看起來不會(huì)很熟悉回怜,沒關(guān)系大年,輸入代碼后我們會(huì)解釋一下。)
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)
??}
}
要?jiǎng)?chuàng)建 CGRect,您可以使用它的構(gòu)造器并傳入 origin.x翔试,origin.y轻要,size.width 和 size.height 的值。
您將使用 UIColor 類屬性 Blue 來設(shè)置 backgroundColor遏餐。 這是一個(gè)初始化配置為 blue 的 UIColor 實(shí)例伦腐。 有很多常用的 UIColor 類屬性,如 green失都,black 和 clear。
構(gòu)建并運(yùn)行應(yīng)用程序(Command-R)幸冻。 您將看到一個(gè)藍(lán)色矩形粹庞,它是 UIView 的實(shí)例。 因?yàn)?UIView 的邊框的起點(diǎn)是(160,240)洽损,所以矩形的左上角為相對(duì)于其父視圖 向右偏移 160 點(diǎn) 向下偏移 240 點(diǎn)庞溜。 根據(jù) size 屬性,視圖將向右延伸100點(diǎn)碑定,從 origin 向下延伸150點(diǎn)(圖3.6)流码。
圖3.6具有一個(gè)UIView的WorldTrotter
請(qǐng)注意,這些值是點(diǎn)而不是像素延刘。 如果值是以像素為單位漫试,則它們?cè)诓煌直媛实娘@示(如 Retina versus 與 non-Retina膜 )上不一致。 點(diǎn)是度量的相對(duì)單位; 它是不同數(shù)量的像素碘赖,這取決于顯示器中有多少個(gè)像素驾荣。 大小,位置普泡,線條和曲線總是以點(diǎn)表示播掷,以便適配不同分辯率的設(shè)備。
圖3.7表示您創(chuàng)建的視圖層級(jí)撼班。
圖3.7當(dāng)前視圖層級(jí)
UIView 的每個(gè)實(shí)例都有一個(gè) superview 屬性歧匈。 當(dāng)您將視圖添加為另一個(gè)視圖的子視圖時(shí),反向關(guān)系將自動(dòng)建立砰嘁。 在當(dāng)前情況下件炉,UIView 的
superview 是 UIWindow。
我們來試驗(yàn)視圖層級(jí)般码。 首先妻率,在 ViewController.swift 中,使用不同的 frame 和背景顏色創(chuàng)建另一個(gè) UIView 實(shí)例板祝。
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)
}
構(gòu)建并再次運(yùn)行宫静。 除了藍(lán)色矩形之外,您將在窗口的左上角附近看到一個(gè)綠色的正方形。 圖3.8顯示了更新后的視圖層級(jí)孤里。
圖3.8 更新后的視圖層級(jí)伏伯,兩個(gè)子視圖作為兄弟
現(xiàn)在,您將調(diào)整視圖層次結(jié)構(gòu)捌袜,以使 UIView 的一個(gè)實(shí)例是另一個(gè) UIView 的子視圖说搅,而不是視圖控制器的視圖砂沛。 在 ViewController.swift 中仁烹,添加 secondView 作為 firstView 的子視圖挤庇。
...
let secondView = UIView(frame: secondFrame)
secondView.backgroundColor = UIColor.green
view.addSubview(secondView)
firstView.addSubview(secondView)
您的視圖層級(jí)現(xiàn)在有四層深香府,如圖3.9所示双絮。
圖3.9 一個(gè)UIView作為另一個(gè)的子視圖
構(gòu)建并運(yùn)行應(yīng)用程序器紧。 請(qǐng)注意邓尤,secondView 在屏幕上的位置已更改(圖3.10)畔况。 視圖的 邊框(frame) 是相對(duì)與其父級(jí)視圖的敦跌,所以 secondView 的左上角(20,30)是以 firstView左上角為參照的澄干。
圖3.10具有新層級(jí)的WorldTrotter
(如果 綠色的 UIView 實(shí)例看起來比以前更小,這只是一個(gè)幻覺柠傍,其實(shí)它的大小沒有改變麸俘。)
現(xiàn)在您已經(jīng)了解了視圖和視圖層級(jí)的基礎(chǔ)知識(shí),可以開始在 WorldTrotter 的界面上工作了惧笛。 但不是以編程方式構(gòu)建界面从媚,您將如第一章一樣去使用 Interface Builder 來直觀地布局界面。
在 ViewController.swift 中徐紧,首先刪除練習(xí)代碼静檬。
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)
}
現(xiàn)在讓我們添加一些視圖到界面并設(shè)置它們的邊框。
打開 Main.storyboard并级。 在畫布的底部拂檩,確保 View as 按鈕配置為顯示 iPhone 7 設(shè)備。
從對(duì)象庫中嘲碧,拖放五個(gè) UILabel 的實(shí)例到畫布上稻励。 將其文本設(shè)置為圖3.11所示,將它們?cè)诮缑娴纳习氩糠执怪狈胖糜⑺鼈兯椒胖谩?/p>
圖3.11添加標(biāo)簽到界面
選擇頂部標(biāo)簽望抽,以便您可以在 Interface Builder 中看到它的邊框。 打開其 大小檢查器(size inspector) ——公用程序區(qū)域中的第五個(gè)選項(xiàng)卡履婉。 (實(shí)用程序選項(xiàng)卡的鍵盤快捷鍵為 Command-Option 加上選項(xiàng)卡號(hào)煤篙,尺寸檢查器是第五個(gè)選項(xiàng)卡,因此鍵盤快捷鍵為 Command-Option-5毁腿。)win+Alt+5
在 View 部分下辑奈,找到 Frame Rectangle苛茂。 (如果沒有看到,可能需要從 show 彈出菜單中選擇它)鸠窗。顯示的值是視圖的 邊框(frame)妓羊,它們決定了屏幕上的視圖位置(圖3.12)。
圖3.12查看邊框值
在 iPhone 7 模擬器上構(gòu)建并運(yùn)行應(yīng)用程序稍计。 模擬器上的界面與 Interface Builder 中布置的界面看起來是一樣的躁绸。
自定義標(biāo)簽
通過自定義視圖屬性,讓界面看起來更漂亮臣嚣。
在 Main.storyboard 中净刮,選擇背景視圖。 打開屬性檢查器并給應(yīng)用程序一個(gè)新的背景顏色:查找并單擊 Background 下拉列表并單擊 Other...茧球。選擇第二個(gè)選項(xiàng)卡(Color Sliders 選項(xiàng)卡)庭瑰,并從下拉列表中選擇 RGB Sliders。 在底部附近的 Hex Color # 框中輸入 F5F4F1 (圖3.13)抢埋。 這將給背景一個(gè)溫暖的灰色。
圖3.13更改背景顏色
您可以同時(shí)自定義所選視圖的常用屬性督暂。 您將使用它來使許多標(biāo)簽擁有更大的字體大小以及燒焦的橙色文本顏色揪垄。
通過在文檔大綱中按住Win鍵單擊它們來選擇頂部的兩個(gè)和底部?jī)蓚€(gè)標(biāo)簽。 確保屬性檢查器已打開逻翁,并更新文本顏色:在 Label 部分下饥努,找到 Color 并打開彈出菜單。 再次選擇 Color Sliders 選項(xiàng)卡八回,在 Hex Color # 輸入 E15829 酷愧。
現(xiàn)在我們來更新字體。 選擇 212 和 100 這兩個(gè)標(biāo)簽缠诅。 在屬性檢查器的 Label 部分下溶浴,查找 Font,然后單擊當(dāng)前字體旁邊的文本圖標(biāo)管引。 在出現(xiàn)的彈出框中士败,Size 改為 70(圖3.14)。 選擇剩余的三個(gè)標(biāo)簽褥伴。 打開他們的 Font 彈出窗口并修改 Size 為 36谅将。
圖3.14自定義標(biāo)簽的字體
現(xiàn)在字體大小較大,文本不再符合標(biāo)簽的范圍重慢。 您可以手動(dòng)調(diào)整標(biāo)簽大小饥臂,但有一種更簡(jiǎn)單的方法。
選擇畫布上的頂部標(biāo)簽似踱。 從 Xcode 的 Editor 菜單中隅熙,選擇 Size to Fit Content(Command- =)稽煤。 這將調(diào)整標(biāo)簽的大小以完全適合其文本內(nèi)容。 對(duì)其他四個(gè)標(biāo)簽重復(fù)此過程猛们。 (您可以選擇所有四個(gè)標(biāo)簽來一次性調(diào)整它們的大小念脯。)現(xiàn)在移動(dòng)標(biāo)簽,使其再次垂直對(duì)齊并水平居中(圖3.15)弯淘。
圖3.15 更新標(biāo)簽邊框
在 iPhone 7 模擬器上構(gòu)建并運(yùn)行應(yīng)用程序绿店。 然后在 iPhone 7 Plus 模擬器上構(gòu)建并運(yùn)行應(yīng)用程序。 請(qǐng)注意庐橙,標(biāo)簽不再居中——而是在偏左的位置假勿。
你剛剛看到絕對(duì)邊框的兩個(gè)主要問題。 首先态鳖,當(dāng)內(nèi)容發(fā)生變化時(shí)(如改變字體大凶唷),邊框不會(huì)自動(dòng)更新浆竭。 第二浸须,視圖在不同尺寸的屏幕上看起來并不是很好。
一般來說邦泄,您不應(yīng)該在您的視圖中使用絕對(duì)邊框删窒。相反,您應(yīng)該使用自動(dòng)布局顺囊,根據(jù)您為每個(gè)視圖指定的約束來靈活地計(jì)算邊框肌索。例如,對(duì)于 WorldTrotter 來說特碳,你真正想要的是讓標(biāo)簽保持與屏幕頂端相同的距離诚亚,并保持水平居中在他們的父視圖中。如果標(biāo)簽的字體或文本發(fā)生變化午乓,它們也應(yīng)該進(jìn)行更新站宗。這是在下一節(jié)中您將完成的任務(wù)。
自動(dòng)布局系統(tǒng)
在你可以修改標(biāo)簽讓它們靈活地布局之前硅瞧,你需要學(xué)習(xí)一些關(guān)于自動(dòng)布局系統(tǒng)的理論份乒。正如您在第1章中看到的,絕對(duì)坐標(biāo)使您的布局變得很脆弱腕唧,因?yàn)樗麄兗俣崆爸榔聊坏拇笮 ?/p>
使用自動(dòng)布局或辖,您可以用相對(duì)的方式描述視圖的布局,以便在運(yùn)行時(shí)確定它們的邊框枣接,因此邊框可以按照正在運(yùn)行的設(shè)備的屏幕大小來調(diào)整應(yīng)用程序的布局颂暇。
對(duì)齊矩形和布局屬性
自動(dòng)布局系統(tǒng)基于對(duì)齊矩形。 該矩形由幾個(gè)布局屬性定義(圖3.16)但惶。
圖3.16定義視圖的對(duì)齊矩形的布局屬性
Width/Height
這些值確定對(duì)齊矩形的大小耳鸯。
Top/Bottom
Left/Right
這些值確定對(duì)齊矩形的給定邊和層級(jí)中另一視圖的對(duì)齊矩形之間的間距湿蛔。
CenterX
CenterY
這些值確定對(duì)齊矩形的中心點(diǎn)。
FirstBaseline
LastBaseline
這些值與大多數(shù)(而不是全部)視圖的底部屬性相同县爬。 例如阳啥,UITextField 將其基線定義為其顯示的文本的底部,而不是對(duì)齊矩形的底部财喳。 這使得“下降”(在基線下方的“g”和“p”字母的部分)不被文本字段下方的視圖遮蔽察迟。 對(duì)于多行文本標(biāo)簽和文本視圖,第一個(gè)和最后一個(gè)基準(zhǔn)指的是文本的第一行和最后一行耳高。 在所有其他情況下扎瓶,第一個(gè)和最后一個(gè)基線是相同的。
Leading
Trailing
這些值是語言特定的屬性泌枪。 如果設(shè)備設(shè)置為從左到右讀取的語言(例如概荷,英文),則 前導(dǎo)(Leading) 屬性與左側(cè)屬性相同碌燕, 尾隨(Trailing) 屬性與右側(cè)屬性相同误证。 如果語言從左到右(例如阿拉伯語)讀取,則前導(dǎo)屬性在右側(cè)修壕,尾部屬性在左側(cè)雷厂。 界面生成器(Interface Builder) 自動(dòng)優(yōu)先于左和右的前導(dǎo)和尾隨,一般來說叠殷,您也應(yīng)該這樣做。
默認(rèn)情況下诈皿,每個(gè)視圖都有一個(gè)對(duì)齊矩形林束,每個(gè)視圖層次結(jié)構(gòu)都使用自動(dòng)布局。
對(duì)齊矩形與邊框非常相似稽亏。 事實(shí)上壶冒,這兩個(gè)矩形通常是一樣的。 而邊框包含整個(gè)視圖截歉,而對(duì)齊矩形僅包含您希望用于對(duì)齊目的的內(nèi)容胖腾。 圖3.17顯示了邊框和對(duì)齊矩形不同的示例。
圖3.17邊框 VS 對(duì)齊矩形
您不能直接定義視圖的對(duì)齊矩形瘪松。 你沒有足夠的信息(如屏幕尺寸)來做到這一點(diǎn)咸作。 相反你應(yīng)該提供一組約束。 總而言之宵睦,這些約束使系統(tǒng)能夠?yàn)橐晥D層次結(jié)構(gòu)中的每個(gè)視圖確定布局屬性记罚,從而確定對(duì)齊矩形。
約束
約束(constraint) 定義視圖層級(jí)中的特定關(guān)系壳嚎,可用于確定一個(gè)或多個(gè)視圖的布局屬性桐智。 例如末早,您可以添加一個(gè)約束,如 “這兩個(gè)視圖之間的垂直間距應(yīng)始終為8點(diǎn)”说庭,或者 “這些視圖必須始終具有相同的寬度”然磷。也可以使用約束來給視圖一個(gè)固定的 大小,如 “這個(gè)視圖的高度應(yīng)該總是44點(diǎn)”刊驴。
您不需要對(duì)每個(gè)布局屬性都添加約束姿搜。 一些值可能直接來自約束; 其他將由相關(guān)布局屬性的值計(jì)算。 例如缺脉,如果視圖的約束設(shè)置其左邊緣和其寬度痪欲,則右邊緣已經(jīng)確定(左邊緣+寬度=右邊緣)。 一般來說攻礼,每個(gè)維度至少需要兩個(gè)約束(水平和垂直)业踢。
如果在考慮所有約束之后,布局屬性仍然存在歧義或缺失值礁扮,那么自動(dòng)布局會(huì)出現(xiàn)錯(cuò)誤和警告知举,并且您的界面在所有設(shè)備上看起來都不會(huì)像您所期望的那樣。 調(diào)試這些問題很重要太伊,您將在本章后面的一些練習(xí)中進(jìn)行實(shí)踐雇锡。
你該如何去設(shè)置約束呢? 讓我們來看看如何使用您在畫布上布置的標(biāo)簽僚焦。
首先锰提,描述您想要的視圖看起來與屏幕大小無關(guān)。例如芳悲,你可能會(huì)說你想要上面的標(biāo)簽是:
- 距離屏幕頂部 8 點(diǎn)
- 在它的父視圖中水平居中
- 與文本一樣寬和高
要將此描述轉(zhuǎn)換為 Interface Builder 中的約束立肘,它將有助于了解如何查找視圖的 最近鄰居(nearest neighbor)。 nearest neighbor 是指定方向上最接近的同級(jí)視圖(圖3.18)名扛。
圖3.18最近鄰居
如果視圖在指定方向上沒有任何兄弟姐妹谅年,則最近的鄰居是其父級(jí)視圖,也是其容器肮韧。
現(xiàn)在您可以明確標(biāo)簽的約束:
- 標(biāo)簽的頂部邊緣應(yīng)距離其最近的鄰居(這是其容器 - ViewController 的視圖)8點(diǎn)融蹂。
- 標(biāo)簽的中心應(yīng)與其父視圖中心相同。
- 標(biāo)簽的寬度應(yīng)等于其字體大小呈現(xiàn)的文本的寬度弄企。
- 標(biāo)簽的高度應(yīng)等于其字體大小呈現(xiàn)的文本的高度超燃。
如果你考慮第一個(gè)和第四個(gè)約束,你可以看到?jīng)]有必要明確地約束標(biāo)簽的底邊桩蓉。 它將根據(jù)標(biāo)簽上邊緣和標(biāo)簽高度的約束來確定淋纲。 類似地,第二和第三約束一起確定標(biāo)簽的右邊緣和左邊緣院究。
現(xiàn)在您知道如何去設(shè)計(jì)頂部標(biāo)簽洽瞬,您將添加這些約束本涕。 可以使用 Interface Builder 或代碼的方式添加約束。 Apple 建議您盡可能使用 Interface Builder 添加約束伙窃,這就是您將在此處執(zhí)行的操作菩颖。 但是,如果您的視圖是以編程方式創(chuàng)建和配置的为障,則可以在代碼中添加約束晦闰。 在第六章中,你會(huì)練習(xí)這種方法鳍怨。
在 Interface Builder 中添加約束
讓我們開始約束這個(gè)頂部的標(biāo)簽呻右。
選擇畫布上的頂部標(biāo)簽。 在畫布的右下角找到自動(dòng)布局約束菜單(圖3.19)鞋喇。
圖3.19自動(dòng)布局約束菜單
單擊
圖標(biāo)(左側(cè)第四個(gè))以顯示 Add New Constraints 菜單声滥。 此菜單顯示標(biāo)簽的當(dāng)前大小和位置。
在 Add New Constraints 菜單的頂部有四個(gè)值侦香,它們描述標(biāo)簽與畫布上最近鄰居的當(dāng)前間距落塑。 對(duì)于此標(biāo)簽,您只對(duì) top 值感興趣罐韩。
要將此值變?yōu)榧s束憾赁,請(qǐng)單擊頂部的紅色支柱,將值與中間的正方形分開散吵。 支柱將成為一條紅色實(shí)線龙考。
在菜單中間找到標(biāo)簽的 Width 和 Height。 Width 和 Height 旁邊的值表示當(dāng)前畫布值矾睦。 要將標(biāo)簽的寬度和高度限制為當(dāng)前畫布值洲愤,請(qǐng)選中 Width 和 Height 旁邊的框。點(diǎn)擊菜單底部的按鈕 Add 3 Constraints顷锰。
此時(shí),您尚未指定足夠的約束來完全確定對(duì)齊矩形亡问。 標(biāo)簽周圍的紅色輪廓表示其對(duì)齊矩形未完全定義官紫,而 Interface Builder 將幫助找出問題所在。
注意 Interface Builder 的右上角黃色警告標(biāo)志(圖3.20)州藕。 點(diǎn)擊此圖標(biāo)顯示問題:
Horizontal position is ambiguous for "212".
“212” 的水平位置不明確
束世。
圖3.20 水平方向不確定
您添加了兩個(gè)垂直約束(頂邊約束和高度約束),但是您只添加了一個(gè)水平約束(寬度約束)床玻。 只有一個(gè)約束使得標(biāo)簽的水平位置不明確毁涉。 您將通過在標(biāo)簽和其父級(jí)視圖之間添加中心對(duì)齊約束來解決此問題。
選中頂部標(biāo)簽后锈死,單擊
圖標(biāo)(左側(cè)的第三個(gè)圖標(biāo))顯示 對(duì)齊(Align) 菜單贫堰。 如果您選擇了多個(gè)視圖穆壕,則此菜單將允許您在視圖中對(duì)齊屬性。 因?yàn)槟贿x擇了一個(gè)標(biāo)簽其屏,所以您唯一的選擇是在其容器內(nèi)對(duì)齊視圖喇勋。
在 Align 菜單中,檢查 Horizontally in Container(現(xiàn)在請(qǐng)勿單擊 Add 1 Constraint)偎行。 添加此約束后川背,將有足夠的約束來完全確定對(duì)齊矩形。 要確保標(biāo)簽的邊框與指定的約束匹配蛤袒,請(qǐng)從 Align 菜單中打開 Update Frames 彈出菜單熄云,然后選擇 Items of New Constraints。 這將重新定位標(biāo)簽以匹配已添加的約束妙真。 現(xiàn)在單擊 *Add 1 Constraint * 以添加居中約束并重新定位標(biāo)簽缴允。
標(biāo)簽的約束都是藍(lán)色的,因?yàn)闃?biāo)簽的對(duì)齊矩形已經(jīng)完全指定隐孽。 此外癌椿,Interface Builder 右上角的警告現(xiàn)在已經(jīng)沒了。
在 iPhone 7 模擬器和 iPhone 7 Plus 模擬器上構(gòu)建和運(yùn)行應(yīng)用程序菱阵。 頂部標(biāo)簽在兩個(gè)模擬器中都能保持居中踢俄。
內(nèi)在內(nèi)容大小
雖然頂部標(biāo)簽的位置是靈活的,但它的大小不是晴及。 這是因?yàn)槟呀?jīng)向標(biāo)簽添加了明確的寬度和高度限制都办。 如果文字或字體改變,您將處于與之前相同的位置虑稼。 邊框的大小是絕對(duì)的琳钉,所以邊框不會(huì)隨內(nèi)容修改而變化。
這是視圖的 內(nèi)在內(nèi)容大小(intrinsic content size) 發(fā)揮作用的地方蛛倦。 您可以將內(nèi)在內(nèi)容大小視為視圖“想要”的大小歌懒。 對(duì)于標(biāo)簽,此大小是以給定字體呈現(xiàn)的文本的大小溯壶。 對(duì)于圖像及皂,就是圖像本身的大小。
視圖的內(nèi)在內(nèi)容大小作為隱式的寬度和高度限制且改。 如果不明確指定寬度的約束验烧,則視圖將是其固有寬度。 高度也一樣又跛。
了解這些之后碍拆,我們可以通過刪除顯式的寬度和高度約束,讓頂部標(biāo)簽具有靈活的大小。
在 Main.storyboard 中感混,選擇標(biāo)簽上的寬度約束端幼。 您可以通過單擊畫布上的約束來執(zhí)行此操作。 或者浩习,在文檔大綱中静暂,您可以單擊 212 標(biāo)簽旁邊的倒三角形,然后列出標(biāo)簽的約束列表(圖3.21)谱秽。
圖3.21選擇寬度約束
選擇寬度約束后洽蛀,按 Delete 鍵。 對(duì)于高度約束也是一樣的疟赊。
請(qǐng)注意郊供,標(biāo)簽的約束仍然是藍(lán)色。 因?yàn)閷挾群透叨仁菑臉?biāo)簽的固有內(nèi)容大小推斷出來的近哟,所以仍然有足夠的約束來確定標(biāo)簽的對(duì)齊矩形驮审。
視圖錯(cuò)位
如您所見,藍(lán)色約束表示視圖的對(duì)齊矩形是完全指定的吉执。 橙色約束通常表示 視圖錯(cuò)位(misplaced view)疯淫。 這意味著Interface Builder中視圖的邊框與
Auto Layout 所計(jì)算的邊框不同。
視圖錯(cuò)位很容易解決戳玫。因?yàn)樵谑褂米詣?dòng)布局時(shí)熙掺, 這是一個(gè)很常見的問題。
讓您的頂部標(biāo)簽出現(xiàn)錯(cuò)位置視圖咕宿,以便您可以看到如何解決此問題币绩。 使用調(diào)整大小控件調(diào)整畫布上的頂部標(biāo)簽大小,并在畫布的右上角查找黃色警告府阀。 單擊此警告圖標(biāo)以顯示問題:
Frame for "212" will be different at run time (Figure 3.22).
運(yùn)行時(shí)“212”的邊框?qū)⒉煌▓D3.22)
缆镣。
圖3.22 視圖錯(cuò)位警告
如警告所述,運(yùn)行時(shí)的邊框與畫布上指定的邊框不同试浙。 如果仔細(xì)觀察董瞻,您將看到一條橙色的虛線,表示運(yùn)行時(shí)邊框是什么樣子的田巴。
構(gòu)建并運(yùn)行應(yīng)用程序力细。 請(qǐng)注意,盡管您在 Interface Builder 中提供了新的邊框固额,標(biāo)簽仍然會(huì)居中。 這可能看起來很棒——畢竟這就是你想要的結(jié)果煞聪。 但是斗躏,當(dāng)您繼續(xù)去構(gòu)建視圖時(shí),在 Interface Builder 中指定的與自動(dòng)布局計(jì)算出來的約束之間的脫節(jié)會(huì)導(dǎo)致問題的發(fā)生。 我們來解決這個(gè)問題啄糙。
回到 故事板(storyboard)笛臣,選擇畫布上的頂部標(biāo)簽。 單擊
圖標(biāo)(最左側(cè)的圖標(biāo))隧饼,更新標(biāo)簽的邊框沈堡,以匹配約束計(jì)算出來的邊框。
使用自動(dòng)布局時(shí)燕雁,您將非常習(xí)慣更新視圖的邊框诞丽。 建議:如果您嘗試更新沒有足夠約束的視圖的邊框,您會(huì)得到意料之外的結(jié)果拐格。 如果發(fā)生這種情況僧免,請(qǐng)撤消更改并檢查約束以查看缺失的內(nèi)容。
此時(shí)捏浊,頂部標(biāo)簽形狀良好懂衩。 它有足夠的約束來確定其對(duì)齊矩形,并且視圖正在布置您想要的方式金踪。
熟練使用自動(dòng)布局需要很多經(jīng)驗(yàn)浊洞,因此在下一部分中,您將從頂部標(biāo)簽中刪除約束胡岔,然后向所有標(biāo)簽添加約束法希。
增加更多的約束
我們來看看其余的視圖的約束。 在執(zhí)行此操作之前姐军,先刪除頂部標(biāo)簽的現(xiàn)有約束铁材。
選擇畫布上的頂部標(biāo)簽。 打開 Resolve Auto Layout Issues 菜單奕锌,然后從 Selected Views 部分中選擇 Clear
Constraints(圖3.23)著觉。
圖3.23 清除約束
您將通過兩個(gè)步驟將約束添加到所有視圖。 首先惊暴,您將在父級(jí)視圖中水平放置頂部標(biāo)簽饼丘。 然后,您將添加約束辽话,將每個(gè)標(biāo)簽的頂部定位到其最近的鄰居肄鸽,同時(shí)對(duì)齊所有標(biāo)簽的中心。
選擇頂部標(biāo)簽油啤。 打開 Align 菜單典徘,并在容器中選擇 Horizontally in Container,設(shè)置為常量 0.確保沒有選中 Update Frames; 請(qǐng)記住益咬,您不會(huì)想更新沒有足夠約束的視圖的邊框逮诲,而這個(gè)約束肯定不會(huì)提供足夠的信息來計(jì)算對(duì)齊矩形。 接著點(diǎn)擊 Add 1 Constraint。
現(xiàn)在選擇畫布上的所有五個(gè)標(biāo)簽梅鹦。 同時(shí)添加多個(gè)視圖的約束是非常方便的裆甩。 打開 Add New Constraints 菜單并進(jìn)行以下選擇:
- 選擇頂部支柱并確保它為 8
- 從 Align 菜單中選擇 Horizontal Centers。
- 從 Update Frames 菜單中齐唆,選擇 Items of New Constraints嗤栓。
您的菜單應(yīng)該會(huì)像 圖3.24 一樣。 完成后箍邮,單擊 Add 9 Constraints茉帅。 這將為視圖添加約束并更新其邊框以響應(yīng)自動(dòng)布局更改。
圖3.24使用 Add New Constraints 菜單添加更多約束
在 iPhone 7 模擬器上構(gòu)建并運(yùn)行應(yīng)用程序媒殉。 視圖將會(huì)居中担敌。 接著在 iPhone 7 Plus 模擬器上構(gòu)建并運(yùn)行應(yīng)用程序。 與本章之前不同廷蓉,所有標(biāo)簽都居中在較大的屏屏幕上全封。
自動(dòng)布局是每個(gè)iOS開發(fā)人員的關(guān)鍵技術(shù)。 它可以幫助您創(chuàng)建靈活的布局桃犬,適用于各種設(shè)備和界面大小刹悴。 掌握它也需要很多的練習(xí)。 在使用本書時(shí)攒暇,您將獲得使用自動(dòng)布局的豐富經(jīng)驗(yàn)土匀。
青銅挑戰(zhàn):更多的自動(dòng)布局實(shí)踐
從 ViewController 界面中刪除所有約束,然后將其重新插入形用。嘗試在不看本書的情況下完成就轧。