在本章中,您將要編寫一個(gè)名為 Quiz 的 iOS應(yīng)用程序。 此應(yīng)用程序?qū)@示一個(gè)問(wèn)題,然后在用戶點(diǎn)擊按鈕時(shí)顯示答案。 點(diǎn)擊另一個(gè)按鈕將向用戶顯示一個(gè)新問(wèn)題(圖1.1)。
圖1.1您的第一個(gè)應(yīng)用程序:Quiz

當(dāng)您編寫iOS應(yīng)用程序時(shí),您必須想到兩個(gè)基本問(wèn)題:
- 如何正確創(chuàng)建和配置對(duì)象? (例如:“我想要一個(gè)
Next Question按鈕”。) - 如何使我的應(yīng)用程序響應(yīng)用戶交互? (例如:當(dāng)用戶點(diǎn)擊按鈕時(shí),我希望這段代碼被執(zhí)行?!埃?/li>
這本書大部分都是為了回答這些問(wèn)題。
創(chuàng)建一個(gè)Xcode項(xiàng)目
打開 Xcode,然后從 File 菜單中選擇 New→ Project....(如果 Xcode 自動(dòng)打開到歡迎界面,請(qǐng)直接選擇 Create a new Xcode project。
將出現(xiàn)一個(gè)新的工作區(qū)窗口,工作表將從工具欄向下滑動(dòng)。 在頂部,找到 iOS 部分,然后找到 Application 區(qū)域(圖1.2)。 被有好幾個(gè)應(yīng)用模板供您選擇。 選擇 Single View Application。
圖1.2創(chuàng)建項(xiàng)目

這本書是為 Xcode 8.1 創(chuàng)建的。 這些模板的名稱可能隨著新的 Xcode 版本而改變。 如果您沒有看到 Single View Application 模板,請(qǐng)使用看起來(lái)最簡(jiǎn)單的模板。
單擊 Next,然后在下一個(gè)工作表 Product Name 中輸入 Quiz(圖1.3)。接著是組織名稱和標(biāo)識(shí)符。 您可以使用 Big Nerd Ranch 或任何您想要的組織名稱。 對(duì)于組織標(biāo)識(shí)符,您可以使用 com.bignerdranch* 或 *com.yourcompanynamehere。
從 Language 彈出菜單中,選擇 Swift,然后從 Devices 彈出菜單中選擇 Universal。 確保未選中 Use Core Data 復(fù)選框。
圖1.3配置新項(xiàng)目

單擊 Next,在最后的工作表中,將項(xiàng)目保存到你計(jì)劃好的存儲(chǔ)本書中例子的目錄中。 單擊 Create 以創(chuàng)建 Quiz 項(xiàng)目。
您的新項(xiàng)目將在Xcode工作區(qū)窗口中打開(圖1.4)。
圖1.4 Xcode工作區(qū)窗口

工作區(qū)窗口的左側(cè)是 導(dǎo)航區(qū)域(navigator area) 。 該區(qū)域顯示不同的導(dǎo)航器 - 顯示項(xiàng)目不同部分的工具。 您可以通過(guò)選擇導(dǎo)航器選擇器中的一個(gè)圖標(biāo)來(lái)打開導(dǎo)航器,導(dǎo)航器選擇器是導(dǎo)航器區(qū)域上方的欄。
當(dāng)前打開的導(dǎo)航器是項(xiàng)目導(dǎo)航器。 項(xiàng)目導(dǎo)航器顯示構(gòu)成項(xiàng)目的文件(圖1.5)。 您可以選擇其中一個(gè)文件,以在導(dǎo)航區(qū)域右側(cè)的編輯器區(qū)域中打開它們。
項(xiàng)目導(dǎo)航器中的文件可以分組到文件夾中,以幫助您組織項(xiàng)目。 模板為您創(chuàng)建了幾個(gè)組。如果需要,您可以重命名它們,或者添加新組。 這些組只是用于組織文件,并且不以任何方式與文件系統(tǒng)相關(guān)。
圖1.5項(xiàng)目導(dǎo)航器中的Quiz應(yīng)用程序文件

模型 - 視圖 - 控制器
在開始應(yīng)用程序之前,讓我們來(lái)討論應(yīng)用程序架構(gòu)中的一個(gè)關(guān)鍵概念: Model-View-Controller 或 MVC。 MVC 是iOS開發(fā)中使用的一種設(shè)計(jì)模式。 在 MVC 中,每個(gè)實(shí)例都屬于模型層,視圖層或控制器層。 (這里的層簡(jiǎn)單地指一個(gè)或多個(gè)符合相同規(guī)則的對(duì)象。)
-
模型層(model layer)保存數(shù)據(jù),不了解用戶界面或 UI。 在Quiz中,模型將由兩個(gè)有序的字符串列表組成:一個(gè)用于提問(wèn),另一個(gè)用于解答。通常,模型層中的實(shí)例代表用戶世界中的真實(shí)事物。 例如,當(dāng)您為保險(xiǎn)公司編寫應(yīng)用程序時(shí),您的模型幾乎肯定會(huì)包含一個(gè)名為 InsurancePolicy 的自定義類型。 -
視圖層(view layer)包含對(duì)用戶可見的對(duì)象。按鈕,文本字段和滑塊就是視圖對(duì)象或視圖的示例。 ?視圖對(duì)象(view objects) 組成應(yīng)用程序的UI。 在 Quiz 中,顯示問(wèn)題和答案的標(biāo)簽及其下的按鈕都是視圖對(duì)象。 -
控制器層(controller layer)是應(yīng)用程序管理的地方。 控制器對(duì)象或控制器是應(yīng)用程序的管理器。 控制器配置用戶看到的視圖,并確保視圖和模型對(duì)象保持同步。 一般來(lái)說(shuō),控制器通常處理“然后呢?”問(wèn)題。 例如,當(dāng)用戶從列表中選擇一個(gè)項(xiàng)目時(shí),控制器確定用戶接下來(lái)看到什么。
圖1.6顯示了響應(yīng)于用戶輸入的應(yīng)用程序中的控制流程,例如用戶點(diǎn)擊按鈕。
圖1.6 MVC模式

請(qǐng)注意,模型和視圖不直接相互交流; 控制器正好位于它們的中間,接收消息和調(diào)度指令。
設(shè)計(jì)Quiz程序
您將使用MVC模式編寫 Quiz 應(yīng)用程序。 以下是您將要?jiǎng)?chuàng)建和使用的實(shí)例的清單:
- 模型層將由 [String] 的兩個(gè)實(shí)例組成。
- 視圖層將由 UILabel 的兩個(gè)實(shí)例和 UIButton 的兩個(gè)實(shí)例組成。
- 控制器層將由 ViewController 的一個(gè)實(shí)例組成。
圖1.7Quiz對(duì)象圖

圖1.7是 Quiz 應(yīng)用程序如何工作的大圖。 例如,當(dāng)點(diǎn)擊 Next Question 按鈕時(shí),它將觸發(fā) ViewController 中的一個(gè)方法。 一個(gè)方法就是一個(gè)函數(shù)——要執(zhí)行的指令列表。 該方法將從問(wèn)題陣列中檢索一個(gè)新問(wèn)題,并要求頂部標(biāo)簽顯示該問(wèn)題。
這圖表在這章結(jié)整前看起來(lái)沒多大意義,這并不影響后面的學(xué)習(xí)。 在構(gòu)建應(yīng)用程序的時(shí)候,請(qǐng)返回這參考這張圖來(lái)查看應(yīng)用程序的流程。
您將逐步構(gòu)建 Quiz 程序,先從應(yīng)用程序的可視化界面開始。
界面生成器
您正在使用 Single View Application 模板,因?yàn)樗?Xcode 提供的最簡(jiǎn)單的模板。 不過(guò),這個(gè)模板有很大的魔力,因?yàn)橐呀?jīng)為您設(shè)置了一些關(guān)鍵組件。 現(xiàn)在,您只需使用這些組件,而不用深入了解它們的工作原理。 本書的其余部分將會(huì)關(guān)注這些細(xì)節(jié)。
在項(xiàng)目導(dǎo)航器中,單擊一次 Main.storyboard 文件。 Xcode 將打開其稱為 Interface Builder 的圖形樣式編輯器。
Interface Builder 將編輯器區(qū)域分為兩部分:左側(cè)的 文檔概覽(document outline) 和右側(cè)的 畫布(canvas)。
如圖1.8所示。 如果您在編輯區(qū)域看到的內(nèi)容與圖形不符,可能需要單擊 Show Document Outline 按鈕。 (如果您有其他區(qū)域顯示,請(qǐng)不用擔(dān)心。)您還可能需要單擊文檔輪廓中的倒三角以顯示內(nèi)容。
圖1.8顯示Main.storyboard的Interface Builder

您在 Interface Builder 畫布中看到的矩形稱為 scene,并表示目前唯一的“屏幕”或視圖(請(qǐng)注意,創(chuàng)建此項(xiàng)目時(shí)你使用的是 Single View Application 模板)。
在下一節(jié)中,您將了解如何使用 Interface Builder 為應(yīng)用程序創(chuàng)建UI。Interface Builder 允許您將庫(kù)中的對(duì)象拖動(dòng)到畫布上以創(chuàng)建實(shí)例,還可以在這些對(duì)象和代碼之間建立連接。 這些連接可以轉(zhuǎn)換成代碼被用戶交互調(diào)用。
Interface Builder 的一個(gè)關(guān)鍵特性是它在其它文件上并沒有生成代碼。 Interface Builder 是一個(gè)對(duì)象編輯器,可以創(chuàng)建對(duì)象的實(shí)例并操縱其屬性。 完成編輯界面后,它不會(huì)生成與您所做的工作相對(duì)應(yīng)的代碼。 在必要時(shí) .storyboard 文件會(huì)加載到內(nèi)存中的對(duì)象實(shí)例的歸檔。
構(gòu)建界面
讓我們開始你的界面。 您已經(jīng)選擇 Main.storyboard 在畫布中顯示的單個(gè) scene(圖1.9)。
圖1.9 Main.storyboard中的場(chǎng)景

要開始,請(qǐng)確保您的場(chǎng)景的尺寸適用于 iPhone 7。在畫布底部,找到 View as 按鈕。 它可能會(huì)像這樣:View as: iPhone 7 (wC hR)。 (wC hR現(xiàn)在來(lái)說(shuō)沒有意義,我們將在第17章中解釋。)如果iPhone 7 已經(jīng)顯示,那么表明已經(jīng)設(shè)置好了。 如果沒有,請(qǐng)單擊 View as 按鈕,然后從左側(cè)選擇第四個(gè)對(duì)應(yīng)于 iPhone 7 的設(shè)備(圖1.10)。
圖1.10查看 iPhone 7 的 scene

現(xiàn)在是將視圖對(duì)象添加到該空白畫板的時(shí)候了。
創(chuàng)建視圖對(duì)象
確保 Xcode 窗口中的實(shí)用程序區(qū)域可見。 您可能需要單擊窗口右上角

inspector ) 和 庫(kù)( library )。 頂部是檢查器,它顯示在編輯器區(qū)域中選擇的文件或?qū)ο蟮脑O(shè)置。 底部是庫(kù),它列出可以添加到文件或項(xiàng)目的項(xiàng)。
在實(shí)用程序區(qū)域的每個(gè)部分的頂部是各種檢查器和庫(kù)的選擇器(圖1.11)。
圖1.11 Xcode實(shí)用程序區(qū)


選項(xiàng)卡來(lái)查看對(duì)象庫(kù),如圖1.11所示。
對(duì)象庫(kù)包含可以添加到故事板文件中以組成界面的對(duì)象。 通過(guò)向下滾動(dòng)列表或使用庫(kù)底部的搜索欄來(lái)查找 Label 對(duì)象。 在庫(kù)中選擇此對(duì)象并將其拖動(dòng)到畫布上的視圖對(duì)象上。 將標(biāo)簽拖到畫布上并注意當(dāng)標(biāo)簽靠近畫布中心時(shí)出現(xiàn)的虛藍(lán)色線條(圖1.12)。 這些輔助線將幫助您部署您的界面。
圖1.12將標(biāo)簽(label)添加到畫布

根據(jù)輔助線的指引,將標(biāo)簽放置在視圖的水平中心,靠近頂部,如圖1.12所示。 最終,此標(biāo)簽將向用戶顯示問(wèn)題。 將第二個(gè)標(biāo)簽拖到視圖上并將其放置在水平中心,靠近中間。 此標(biāo)簽將顯示答案。
接下來(lái),在對(duì)象庫(kù)中找到 Button 并將兩個(gè)按鈕拖到視圖上。 在每個(gè)標(biāo)簽下方放置一個(gè)。
您現(xiàn)在已將四個(gè)視圖對(duì)象添加到 ViewController 的UI中。 請(qǐng)注意,它們也出現(xiàn)在文檔大綱中。 您的界面應(yīng)如圖1.13所示。
圖1.13構(gòu)建Quiz界面

配置視圖對(duì)象
現(xiàn)在您已經(jīng)創(chuàng)建了視圖對(duì)象,可以配置其屬性了。 視圖的某些屬性(如大小,位置和文本)可以直接在畫布上更改。 例如,您可以通過(guò)在畫布或文檔輪廓中選擇對(duì)象,然后在畫布中拖動(dòng)其角落和邊緣來(lái)調(diào)整對(duì)象的大小。
首先重命名標(biāo)簽和按鈕。 雙擊每個(gè)標(biāo)簽并用 ??? 替換文本。 然后雙擊上方的按鈕并將其名稱更改為 Next Question。 重命名下面的按鈕為 Show Answer。 結(jié)果如圖1.14所示。
圖1.14重命名標(biāo)簽和按鈕

您可能已經(jīng)注意到,因?yàn)槟牧藰?biāo)簽和按鈕中的文本,因此它們的寬度已經(jīng)不再整齊地居中在 scene 中。 點(diǎn)擊并拖動(dòng)它們以使它們?cè)俅尉又?,如圖1.15所示。
圖1.15定位標(biāo)簽和按鈕

在模擬器上運(yùn)行
要測(cè)試您的UI,您將要在 Xcode 的 iOS模擬器 上運(yùn)行 Quiz。
要準(zhǔn)備在模擬器上運(yùn)行 Quiz,請(qǐng)?jiān)?Xcode 工具欄上找到當(dāng)前的方案彈出菜單(圖1.16)。
圖1.16選擇了iPhone 7方案

如果它顯示了 iPhone 7 一樣的東西,那么這個(gè)項(xiàng)目就被設(shè)置為在模擬器上運(yùn)行。 如果它顯示 Christian's iPhone,那就要點(diǎn)擊并從彈出菜單中選擇 iPhone 7。 在本書中,iPhone 7 方案將是您的模擬器默認(rèn)設(shè)置。
單擊工具欄中的三角形播放按鈕。 這將構(gòu)建(編譯)然后運(yùn)行應(yīng)用程序。 你會(huì)經(jīng)常做這個(gè),鍵盤快捷方式為 Command-R。
模擬器啟動(dòng)后,您將看到界面具有您添加的所有視圖,與你在 Interface Builder 中看到的一樣。
現(xiàn)在回到當(dāng)前的方案彈出菜單,選擇 iPhone 7 Plus 作為您的模擬器。 再次運(yùn)行應(yīng)用程序,您會(huì)注意到,您添加的視圖仍然存在,但它們不像在 iPhone 7 一樣居中。這是因?yàn)闃?biāo)簽和按鈕目前在屏幕上具有固定位置,并且不會(huì)在主視圖保持居中。 要糾正此問(wèn)題,您將使用一種稱為 自動(dòng)布局(Auto Layout) 的技術(shù)。
自動(dòng)布局簡(jiǎn)介
到目前為止,您的界面在 Interface Builder 畫布中看起來(lái)不錯(cuò)。 但是,iOS設(shè)備的屏幕尺寸越來(lái)越大,而我們希望應(yīng)用程序能支持所有屏幕尺寸和方向,甚至支持多種設(shè)備類型。 無(wú)論運(yùn)行應(yīng)用程序的設(shè)備的屏幕尺寸或方向如何,都需要保證視圖對(duì)象的布局正確。 解決方法就是使用自動(dòng)布局。
自動(dòng)布局通過(guò)為 scene 中的每個(gè)視圖對(duì)象指定位置和大小約束來(lái)工作。 這些約束可以相對(duì)應(yīng)于鄰近視圖或容器視圖。 容器視圖只是一個(gè)視圖對(duì)象,顧名思義,它包含另一個(gè)視圖。 例如,查看 Main.storyboard 的文檔大綱(圖1.17)。
圖1.17帶容器視圖的文檔布局

您可以在文檔大綱中看到,您添加的標(biāo)簽和按鈕對(duì)于一個(gè) View 對(duì)象是縮進(jìn)的。 此視圖對(duì)象是標(biāo)簽和按鈕的容器,并且對(duì)象可以相對(duì)于此視圖進(jìn)行定位和縮放。
要開始指定自動(dòng)布局約束,請(qǐng)通過(guò)在畫布上或文檔大綱中單擊頂部標(biāo)簽來(lái)選擇頂部標(biāo)簽。 在畫布底部,注意如圖1.18所示的自動(dòng)布局菜單,。
圖1.18自動(dòng)布局菜單


圖標(biāo)顯示如圖1.19所示的 對(duì)齊菜單(Align menu)。
圖1.19將容器中的頂部標(biāo)簽居中

在 Align 菜單中,選中 Horizontally in Container 復(fù)選框,以將標(biāo)簽居中。 然后單擊 Add 1 Constraint 按鈕。 該約束能保證在任何尺寸的屏幕上,以任何方向,標(biāo)簽將水平居中。
現(xiàn)在,您需要添加更多的約束來(lái)使下面的標(biāo)簽和按鈕相對(duì)于頂部標(biāo)簽居中,并鎖定它們之間的間距。 通過(guò)按住 Command 鍵單擊選中這四個(gè)視圖對(duì)象,然后單擊圖標(biāo)

Add New Constraints 菜單,如圖1.20所示。
圖1.20 添加約束以居中并修復(fù)視圖之間的間距

點(diǎn)擊菜單頂部附近的紅色垂直虛線段。 當(dāng)您點(diǎn)擊該段時(shí),它將變?yōu)閷?shí)心紅色(如圖1.20所示),表示每個(gè)視圖的距離固定在最近的頂層鄰居處。 另外,檢查 Align 框,然后從彈出菜單中選擇 Horizontal Centers。 對(duì)于 Update Frames,請(qǐng)確保已選擇 Items of New Constraints。 最后點(diǎn)擊菜單底部的 Add 7 Constraints 按鈕。
如果您在添加約束時(shí)發(fā)生任何錯(cuò)誤,您可能會(huì)在畫布上看到紅色或橙色的約束和邊框,而不是正確的藍(lán)線。 如果是這種情況,您將需要清除現(xiàn)有的約束,然后再次執(zhí)行上述步驟。 要清除約束,首先選擇背景(容器)視圖。 然后單擊該圖標(biāo)

Resolve Auto Layout Issues 菜單。 在 All Views in View Controller 部分下選擇 Clear Constraints 部分(圖1.21)。 這將清除您添加的任何限制,并讓您重新添加約束。
圖1.21清除約束

自動(dòng)布局可能是一個(gè)難以掌握的工具,這就是為什么您在本書第一章開始使用它。 越早開始接觸使用它,你將有更多的機(jī)會(huì)使用它,并習(xí)慣它。 另外,在應(yīng)用程序變得復(fù)雜之前處理自動(dòng)布局將有助于你更容易地去調(diào)試布局問(wèn)題。
要確認(rèn)您的界面正確展示,請(qǐng)?jiān)?iPhone 7 Plus 模擬器上構(gòu)建并運(yùn)行應(yīng)用程序。 確認(rèn)界面看起來(lái)正確后,在iPhone 7 模擬器上構(gòu)建并運(yùn)行應(yīng)用程序。 標(biāo)簽和按鈕在兩者皆居中。
創(chuàng)建連接
連接(connection) 允許一個(gè)對(duì)象知道另一個(gè)對(duì)象在內(nèi)存中的位置,以便兩個(gè)對(duì)象可以進(jìn)行通信。 您可以在 Interface Builder 中進(jìn)行兩種連接:outlet 和 action。 outlet 是對(duì)象的引用。action 是一種方法,由用戶通過(guò)按鈕或滑塊或選擇器觸發(fā)。
我們先來(lái)創(chuàng)建引用 UILabel 實(shí)例的 outlet。 是時(shí)候離開 Interface Builder 去編寫一些代碼了。
聲明 outlet
在項(xiàng)目導(dǎo)航器中,找到并選中名為 ViewController.swift 的文件。 編輯器區(qū)域?qū)?Interface Builder 更改為 Xcode 的代碼編輯器。
在 ViewController.swift 中,首先刪除模板在 class ViewController :UIViewController { 和 } 之間自動(dòng)生成的代碼,使該文件如下所示:
import UIKit
class ViewController: UIViewController {
}
(為了簡(jiǎn)方使起見,我們不會(huì)再顯示該文件的 import UIKit 這一行 。)
接下來(lái),添加聲明兩個(gè)屬性的代碼。 (在本書中,您將添加的新代碼將以粗體顯示,您將刪除的代碼將被刪除線顯示。)不用擔(dān)心現(xiàn)在就要了解代碼或?qū)傩? 這才剛剛開始
class ViewController: UIViewController {
??@IBOutlet var questionLabel: UILabel!
??@IBOutlet var answerLabel: UILabel!
}
此代碼為 ViewController 的每個(gè)實(shí)例提供一個(gè)名為 questionLabel 的 outlet 和一個(gè)名為 answerLabel 的 outlet。 視圖控制器可以使用每個(gè) outlet 來(lái)引用特定的 UILabel 對(duì)象(即您視圖中的一個(gè)標(biāo)簽)。 @IBOutlet 關(guān)鍵字告訴 Xcode,您將使用 Interface Builder 將這些 outlet 連接到標(biāo)簽對(duì)象。
設(shè)置 outlet
在項(xiàng)目導(dǎo)航器中,選擇 Main.storyboard 重新打開 Interface Builder。
你想讓 questionLabel outlet 指向 UI 頂部的 UILabel 實(shí)例。
在文檔大綱中,找到其中的 View Controller Scene 部分和 View Controller 對(duì)象。 在當(dāng)前的情況下,View Controller 代表 ViewController 的一個(gè)實(shí)例,它是負(fù)責(zé)管理 Main.storyboard 中定義的界面的對(duì)象。
右鍵 從文檔輪廓中的 View Controller 拖動(dòng)到場(chǎng)景中的頂部標(biāo)簽。 當(dāng)標(biāo)簽突出顯示時(shí),松開鼠標(biāo)和鍵盤; 將出現(xiàn)黑色面板。 選擇 questionLabel 來(lái)設(shè)置 outlet,如圖1.22所示。
圖1.22設(shè)置questionLabel

(如果在連接面板中沒有看到 questionLabel,請(qǐng)仔細(xì)檢查您的 ViewController.swift 文件以進(jìn)行修改)。
現(xiàn)在,當(dāng)加載了故事板文件時(shí),ViewController 的 questionLabel outlet 將自動(dòng)引用屏幕頂部的 UILabel 實(shí)例,這將允許 ViewController 告訴標(biāo)簽顯示什么問(wèn)題。
以相同的方式設(shè)置 answerLabel outlet: 右鍵從 ViewController 拖動(dòng)到底部的 UILabel 并選擇 answerLabel(圖1.23)。
圖1.23設(shè)置answerLabel

請(qǐng)注意,是從要設(shè)置的 outlet 的對(duì)象拖動(dòng)到要將該 outlet 指向的對(duì)象。
您的 outlet 全部設(shè)置好后,您需要的下一個(gè)連接涉及到兩個(gè)按鈕。
定義動(dòng)作方法
當(dāng) UIButton 被點(diǎn)擊時(shí),它會(huì)調(diào)用另一個(gè)對(duì)象的方法。 該對(duì)象稱為 目標(biāo)(target)。 觸發(fā)的方法稱為動(dòng)作(action)。 該動(dòng)作是該方法的名稱,該方法包含要在按鈕被點(diǎn)擊時(shí)執(zhí)行的代碼。
在您的應(yīng)用程序中,這兩個(gè)按鈕的目標(biāo)將是 ViewController 的實(shí)例。 每個(gè)按鈕都有自己的動(dòng)作。 我們首先定義兩個(gè)動(dòng)作方法:showNextQuestion(_ :) 和 showAnswer(_ :)。
重新打開 ViewController.swift,并在 outlet 后添加兩種動(dòng)作方法。
class ViewController: UIViewController {
@IBOutlet var questionLabel: UILabel!
??@IBOutlet var answerLabel: UILabel!
@IBAction func showNextQuestion(_ sender: UIButton) {
}
@IBAction func showAnswer(_ sender: UIButton) {
}
}
在進(jìn)行目標(biāo)和動(dòng)作連接后,您將會(huì)使用這些方法。@IBAction 關(guān)鍵字告訴 Xcode 您將在 Interface Builder 中進(jìn)行這些連接。
設(shè)定目標(biāo)和動(dòng)作
切換回 Main.storyboard。 我們先從 Next Question 按鈕開始。 您希望將其目標(biāo)設(shè)為 ViewController,其動(dòng)作為 showNextQuestion(_ :)。
要設(shè)置一個(gè)對(duì)象的目標(biāo),請(qǐng)右鍵從該對(duì)象拖動(dòng)到它的目標(biāo)。 當(dāng)您釋放鼠標(biāo)時(shí),目標(biāo)將被設(shè)置,并出現(xiàn)一個(gè)彈出菜單,讓您選擇一個(gè)動(dòng)作。
在畫布中選擇 Next Question 按鈕,然后拖動(dòng)到在文檔大綱的 View Controller。 當(dāng) View Controller 突出顯示時(shí),釋放鼠標(biāo)按鈕并在彈出菜單中的 Sent Events 下選擇 showNextQuestion:如圖1.24所示。
圖1.24設(shè)置 Next Question 目標(biāo)/動(dòng)作

現(xiàn)在輪到 Show Answer 按鈕。 選擇按鈕,然后從按鈕控制拖動(dòng)到 View Controller。 從彈出菜單中選擇 showAnswer:
連接摘要
ViewController 和視圖對(duì)象之間現(xiàn)在有五個(gè)連接。 您已設(shè)置屬性 answerLabel 和 questionLabel 來(lái)引用標(biāo)簽對(duì)象——這是其中兩個(gè)。 ViewController 是兩個(gè)按鈕的目標(biāo)——這是另外兩個(gè)。 項(xiàng)目的模板創(chuàng)建了一個(gè)附加連接: ViewController 的 view 屬性連接到表示應(yīng)用程序背景的 View 對(duì)象。 這就是所有的五個(gè)連接。
您可以在連接檢查器中檢查這些連接。 在文檔大綱中選擇 View Controller。 然后,在公用程序區(qū)域中,單擊選項(xiàng)卡

圖1.25檢查連接檢查器中的連接

您的故事板文件已完成。 視圖對(duì)象已創(chuàng)建和配置,并已對(duì)控制器對(duì)象進(jìn)行了所有必要的連接。 我們繼續(xù)創(chuàng)建和連接你的模型對(duì)象。
創(chuàng)建模型層
視圖對(duì)象組成UI,因此開發(fā)人員通常使用 Interface Builder 創(chuàng)建,配置和連接視圖對(duì)象。 另一方面,模型層的部分通常以代碼形式設(shè)置。
在項(xiàng)目導(dǎo)航器中,選擇 ViewController.swift。 添加以下代碼,聲明兩個(gè)字符串?dāng)?shù)組和一個(gè)整數(shù)。
class ViewController: UIViewController {
??@IBOutlet var questionLabel: UILabel!
??@IBOutlet var answerLabel: UILabel!
let questions: [String] = [
????"What is 7+7?",
????"What is the capital of Vermont?",
????"What is cognac made from?"
??]
??let answers: [String] = [
????"14",
????"Montpelier",
????"Grapes"
??]
??var currentQuestionIndex: Int = 0
...
}
數(shù)組是包含問(wèn)題和答案的有序列表。 整數(shù)用于跟蹤用戶當(dāng)前的問(wèn)題。
請(qǐng)注意,使用 let 關(guān)鍵字聲明數(shù)組,而使用 var 關(guān)鍵字聲明整數(shù)。 常數(shù)用 let 關(guān)鍵字表示; 其值不能改變。 問(wèn)題和答案數(shù)組是常數(shù)。 本程序中的問(wèn)題和答案不會(huì)改變,實(shí)際上只是不能從初始值改變。
另一方面,變量由 var 關(guān)鍵字表示; 它的值被允許改變。 你將 currentQuestionIndex 屬性變成一個(gè)變量,因?yàn)樗闹当仨毮軌螂S著用戶循環(huán)的問(wèn)題和答案而改變。
實(shí)現(xiàn)動(dòng)作方法
現(xiàn)在你有問(wèn)題和答案,你可以來(lái)實(shí)現(xiàn)它們的動(dòng)作方法。 在 ViewController.swift 中,更新 showNextQuestion(_ :) 和 showAnswer(_ :)。
...
@IBAction func showNextQuestion(_ sender: UIButton) {
??currentQuestionIndex += 1
??if currentQuestionIndex == questions.count {
????currentQuestionIndex = 0
??}
??let question: String = questions[currentQuestionIndex]
??questionLabel.text = question
??answerLabel.text = "???"
}
@IBAction func showAnswer(_ sender: UIButton) {
??let answer: String = answers[currentQuestionIndex]
??answerLabel.text = answer
}.
..
加載第一個(gè)問(wèn)題
應(yīng)用程序啟動(dòng)后,您將要從數(shù)組中加載第一個(gè)問(wèn)題,并使用它來(lái)替換 ??? 問(wèn)題標(biāo)簽中的占位符。 一個(gè)很好的方法是重寫 ViewController 的 viewDidLoad() 方法。 (“覆蓋”表示您正在為方法提供自定義實(shí)現(xiàn)。)將方法添加到 ViewController.swift。
class ViewController: UIViewController {
??...
??override func viewDidLoad() {
??super.viewDidLoad()
????questionLabel.text = questions[currentQuestionIndex]
??}
}
您的應(yīng)用程序的所有代碼現(xiàn)在已經(jīng)完成!
構(gòu)建應(yīng)用程序
如前所述,在 iPhone 7 模擬器上構(gòu)建并運(yùn)行應(yīng)用程序。
如果構(gòu)建出現(xiàn)任何錯(cuò)誤,您可以通過(guò)選擇導(dǎo)航器區(qū)域中的選項(xiàng)卡
,在問(wèn)題導(dǎo)航器中查看它們(圖1.26)。
圖1.26帶有示例錯(cuò)誤和警告的問(wèn)題導(dǎo)航器

單擊問(wèn)題導(dǎo)航器中的任何錯(cuò)誤或警告將被帶到文件和發(fā)生問(wèn)題的代碼行。 通過(guò)將代碼與本章中的代碼進(jìn)行比較,找出并解決任何問(wèn)題(即代碼拼寫錯(cuò)誤?。?。 然后再次嘗試運(yùn)行應(yīng)用程序。 重復(fù)此過(guò)程直到您的應(yīng)用程序編譯成功。
在您的應(yīng)用程序編譯完成后,它將在iOS模擬器中啟動(dòng)。 測(cè)試 Quiz 應(yīng)用程序。 您應(yīng)該能夠點(diǎn)擊 Next Question 按鈕,并在頂部標(biāo)簽中看到一個(gè)新問(wèn)題; 點(diǎn)擊 Show Answer* 應(yīng)該顯示正確的答案。 如果您的應(yīng)用程序無(wú)法正常工作,請(qǐng)檢查 Main.storyboard 中的連接。
你已經(jīng)建立了一個(gè)可運(yùn)行的iOS應(yīng)用程序,花點(diǎn)時(shí)間享受你的成果。
好的,享受夠了。你的應(yīng)用程序可以運(yùn)行,但它需要一些美化和改進(jìn)。
應(yīng)用圖標(biāo)
運(yùn)行 Quiz 時(shí),從模擬器菜單中選擇 Hardware → Home。 你會(huì)看到 Quiz 圖標(biāo)是一個(gè)無(wú)聊的默認(rèn)磁貼。 讓我們給 Quiz 一個(gè)更好的圖標(biāo)。
應(yīng)用程序圖標(biāo)是表示iOS主屏幕上的應(yīng)用程序的簡(jiǎn)單圖像。 不同的設(shè)備需要不同大小的圖標(biāo),其中一些顯示在表1.1中。
表1.1不同設(shè)備的應(yīng)用程序圖標(biāo)

我們已經(jīng)為 Quiz 應(yīng)用準(zhǔn)備了一個(gè)圖標(biāo)圖像文件(大小為120x120)。 您可以從 www.bignerdranch.com/solutions/iOSProgramming6ed.zip 下載此圖標(biāo)(以及其他章節(jié)的資源)。 解壓縮iOSProgramming6ed.zip并在解壓縮的文件夾的 0- Resources/Project App Icons 目錄中找到 Quiz-120.png 文件。
您將將此圖標(biāo)作為資源添加到應(yīng)用程序包中。 一般來(lái)說(shuō),應(yīng)用程序中有兩種文件:代碼和資源。 代碼(如 ViewController.swift)用于創(chuàng)建應(yīng)用程序本身。 資源是應(yīng)用程序在運(yùn)行時(shí)使用的圖像和聲音。
在項(xiàng)目導(dǎo)航器中,找到 Assets.xcassets。 選擇此文件打開它,然后從左側(cè)的資源列表中選擇 AppIcon(圖1.27)。
圖1.27顯示Asset目錄

此面板是 Asset 目錄,您可以在其中管理應(yīng)用程序所需的所有圖像。
將 Quiz-120.png 文件從 Finder 拖到 iPhone App 部分的 2x 插槽上(圖1.28)。 這將將文件復(fù)制到文件系統(tǒng)中的項(xiàng)目目錄中,并在 Asset 目錄中添加對(duì)該文件的引用。 (您可以按住 Control 鍵并單擊 Asset 目錄中的文件,然后選擇在 Show in Finder 以確認(rèn)此選項(xiàng)。)
圖1.28將應(yīng)用圖標(biāo)添加到 Asset 目錄中

再次構(gòu)建并運(yùn)行應(yīng)用程序。 通過(guò)單擊 Hardware → Home,像之前一樣,或使用鍵盤快捷鍵 Command-Shift-H 切換到模擬器的主屏幕(在虛擬機(jī)中 Command 鍵即 Win 鍵)。 你應(yīng)該看到新的圖標(biāo)。
(如果沒有看到圖標(biāo),請(qǐng)關(guān)閉應(yīng)用程序,然后重新構(gòu)建并重新運(yùn)行)。為此,最簡(jiǎn)單的選項(xiàng)是通過(guò)單擊 Simulator → Reset Content and Settings... 這將刪除所有應(yīng)用程序并將模擬器重置為默認(rèn)設(shè)置,您應(yīng)該在下次運(yùn)行應(yīng)用程序時(shí)看到應(yīng)用程序圖標(biāo)。)
啟動(dòng)屏幕
現(xiàn)在該為項(xiàng)目設(shè)置 啟動(dòng)圖片(launch image) 了,它在應(yīng)用程序加載時(shí)顯示。 launch image 在iOS中具有特定的作用:它表示應(yīng)用程序正在啟動(dòng),并描述了用戶在應(yīng)用程序加載后將進(jìn)行交互的UI。 因此,良好的 launch image 是應(yīng)用程序的無(wú)內(nèi)容屏幕截圖。 例如,Clock 應(yīng)用程序的 launch image 顯示底部的四個(gè)選項(xiàng)卡,全部處于未選擇的狀態(tài)。 一旦應(yīng)用程序加載,將選擇正確的選項(xiàng)卡,內(nèi)容變得可見。 (請(qǐng)注意,啟動(dòng)圖片在應(yīng)用程序啟動(dòng)后被替換;它不會(huì)成為應(yīng)用程序的背景圖像。)
完成此操作的一個(gè)簡(jiǎn)單方法是允許 Xcode 使用 launch screen file 為您生成可能的 啟動(dòng)屏幕圖像。
通過(guò)單擊項(xiàng)目導(dǎo)航器中的最頂部的 Quiz 來(lái)打開項(xiàng)目設(shè)置。 在 App Icons and Launch Images 下,從 Launch Screen File 下拉列表中選擇 Main.storyboard(圖1.29)。 現(xiàn)在將從 Main.storyboard 生成啟動(dòng)圖片。
圖1.29設(shè)置啟動(dòng)屏幕文件

很難看到這種更改的結(jié)果,因?yàn)?啟動(dòng)圖片 通常只在很短的時(shí)間內(nèi)顯示。然而,盡管它的角色是如此的短暫,但它仍然是一個(gè)良好的實(shí)踐。
恭喜! 你已經(jīng)寫了你的第一個(gè)應(yīng)用程序,甚至添加了一些細(xì)節(jié)來(lái)改進(jìn)它。 您將在本書后再次用到該 Quiz 應(yīng)用程序。 下一章將為你介紹一些 Swift 編程的基礎(chǔ)知識(shí)。