歡迎回到 macOS 開發(fā)教程初學(xué)者系列 3 部分中的第 2 部分!
在本系列的第 1 部分中,學(xué)習(xí)了如何安裝 Xcode、如何創(chuàng)建新 app、添加 UI、將 UI 連接到代碼、運(yùn)行 app、調(diào)試 app 以及如何獲得幫助。如果其中還有沒掌握的,回去再看一遍第 1 部分。
在本部分中,你將為更復(fù)雜的 app 創(chuàng)建用戶界面。你將學(xué)習(xí)如何讓窗口大小可被調(diào)整,以及設(shè)計(jì)和導(dǎo)航到第二個(gè)窗口,以顯示 app 的偏好設(shè)置。
開始
打開 Xcode 然后在歡迎窗口點(diǎn)擊 Create a new Xcode project 或者選擇 File/New/Project… 就像你在第 1 部分做的,選擇 macOS/Application/Cocoa Application。點(diǎn)擊 Next,命名為 ** EggTimer**,確定語言是 Swift,勾上了 Use Storyboards。點(diǎn)擊 Next 然后選擇存儲(chǔ)項(xiàng)目的位置。
構(gòu)建并運(yùn)行你的新 app,確保一切正常。
EggTimer App
你即將構(gòu)建的的 app 叫做 EggTimer;它從用戶選擇的時(shí)間開始倒計(jì)時(shí),顯示剩余時(shí)間。有一張圖片隨著雞蛋煮熟而變化,一個(gè)聲音在雞蛋煮好時(shí)播放。第二個(gè)窗口用于顯示 app 的偏好設(shè)置。
從項(xiàng)目導(dǎo)航器里打開 Main.storyboard。在第 1 部分中已經(jīng)講過,已經(jīng)存在三個(gè)組件:
- Application Scene
- Window Controller Scene
- View Controller Scene
Application Scene 包含 app 運(yùn)行時(shí)顯示的菜單欄和菜單。Window Controller 是 app 的一部分,定義窗口的行為:如何調(diào)整大小,新窗口如何顯示,app 是否保存窗口大小和位置等。window controller 可以管理多個(gè)窗口,但是如果它們需要不同的屬性,則需要添加另一個(gè) window controller。
View Controller 在窗口內(nèi)顯示用戶界面——你會(huì)在這里為主顯示屏布局 UI。
注意,Window Controller 有一個(gè)指向它的箭頭。這表示它將控制 app 啟動(dòng)時(shí)的初始畫面。你可以在 Document Outline 里選擇 Window Controller,然后到 Attributes Inspector 里勾選它。取消對 Is Initial Controller 的勾選,這個(gè)箭頭就消失了。把它再勾起來吧,因?yàn)槟阆M浅跏伎刂破鳌?/p>
Window Controller
開始布局用戶界面之前,確保你已經(jīng)在項(xiàng)目導(dǎo)航器中選擇了 Main.storyboard。點(diǎn)擊 Window Controller 內(nèi)部以選擇它的窗口。Window Controller 是那個(gè)顯示了文本 “View Controller” 的可視化編輯器,因?yàn)檫@是被它所包含的。對于這個(gè) app,我們不想讓窗口縮小到 346 x 471 像素以下。這也會(huì)是窗口的初始尺寸。
在 Utilities 里選擇 Size Inspector,然后將 Content Size Width 設(shè)置為 346,Content Size Height 設(shè)置為 471。勾選 Minimum Content Size,并確保寬度和高度值與 content size 相同??梢暬庉嬈髦械?Window Controller 的大小已經(jīng)改變了。你現(xiàn)在應(yīng)該需要移動(dòng)一下它,使其不與其他對象重疊。
雖然不是絕對必要,但是如果將 View Controller 調(diào)整為與它包含的 Window Controller 相同的尺寸,看起來會(huì)更輕松一些。單擊視圖控制器,確保在 Document Outline 中選擇了 View。在 Size Inspector 中將寬度和高度分別設(shè)置為 346 和 471。根據(jù)需要移動(dòng)一下位置以查看所有對象?,F(xiàn)在 WindowController 和 ViewController 在可視化編輯器中以相同的大小顯示。
選擇 WindowController 中的 Window,并在 Attributes Inspector 中將其 title 更改為 Egg Timer。將 Autosave name 設(shè)置為 EggTimerMainWindow,啟動(dòng)時(shí)會(huì)記錄上次窗口的大小和位置。
如果你是 iOS 程序員,你會(huì)處理不同的設(shè)備類型、旋轉(zhuǎn)方向的各種屏幕尺寸。在 macOS 編程中,你必須處理無限多種窗口大小和寬高比,這就是為什么我讓這個(gè)窗口的初始尺寸看起來有點(diǎn)奇怪。幸運(yùn)的是,Auto Layout 可以處理這一切。
布局 UI——第 1 部分
基本 UI 由 2 個(gè) stack view 組成。第一個(gè)包含剩余時(shí)間文本和雞蛋圖像。第二個(gè)包含底部的3個(gè)按鈕。從按鈕開始:
- 在 Object Library 里搜索 “Button”
- 拖一個(gè) Gradient button 到 View Controller 里
- 使用 Attributes Inspector,刪除它的圖片并把 title 設(shè)置為 Start。
- 把 font 改為 System 24。
- 展開按鈕以顯示所有文本。
- 選中 Start 按鈕,按兩次 Command-D 以創(chuàng)建 2 個(gè)拷貝。
- 把新按鈕拖出來以便你能看見它們。
- 修改新按鈕的標(biāo)題為 Stop 和 Reset。
- 同時(shí)選中 3 個(gè)按鈕,然后點(diǎn)擊 Editor/Embed In/Stack View。
要讓按鈕填滿 stack view,選中新的 Stack View,然后在 Attributes Inspector 里做如下改變:
- Distribution:Fill Equally
- Spacing:0
單擊 Visual Editor 底部的 Add New Constraints 按鈕,并如圖所示設(shè)置左、右、底和高度約束。選擇 Update Frames: Items of New Constraints 然后點(diǎn)擊 Apply 4 Constraints。
Stack view 現(xiàn)在的位置已經(jīng)正確了,但按鈕比 stack view 要短一些。在 Document Outline 里,按住 Control 從 Start 按鈕拖到 Stack View,然后選擇 Equal Heights。對其它兩個(gè)按鈕執(zhí)行相同操作。
按鈕 stack view 現(xiàn)在就是想要的樣子。
構(gòu)建并運(yùn)行 app。嘗試調(diào)整窗口大?。喊粹o黏在窗口的底部,并自動(dòng)調(diào)整大小以均勻填充寬度。
最后一次操作,在 Attributes Inspector 中取消選中 Enabled 來禁用 Stop 和 Reset 按鈕。在定時(shí)器啟動(dòng)之前啟用它們沒有任何意義。
布局 UI——第 2 部分
第二個(gè) stack view 包含剩余的時(shí)間和圖片。把 Label 拖到 View Controller 中,設(shè)置 Title 為 6:00,然后設(shè)置 Alignment 為 center。當(dāng)前系統(tǒng)字體(San Francisco)使用比例間距的數(shù)字,這意味著,如果你有一個(gè)計(jì)數(shù)器,數(shù)字改變的時(shí)候會(huì)跳躍——這真的很煩人。
把字體切換到 Helvetica Neue 以避免這種情況,設(shè)置字體大小為 100。這將使文本太大無法顯示,因此展開 label field 直到能夠看見。
要添加圖片,請打開 Object Library,在filter field 中 輸入 “image” 進(jìn)行搜索。然乎會(huì)出現(xiàn)幾個(gè)選項(xiàng),但你想要的是 Image View。將其拖到 View Controller,放在 time remaining label 下方。
下載 此項(xiàng)目的資源 (圖片和聲音文件)。解壓文件然后打開 Egg Images 文件夾。在 Xcode 中,點(diǎn)擊 Project Navigator 中的 Assets.xcassets。
將 6 個(gè)圖像文件拖動(dòng)到 Assets library 中。它們現(xiàn)在可以用于這個(gè) app。因?yàn)閳D像文件名包含 “@2x”,所以它們已自動(dòng)分配給每個(gè)圖像資源的 2x 部分。
返回 Main.storyboard,選擇剛剛添加的 Image View,然后單擊 Attributes Inspector 中的 Image 彈出窗口??梢钥吹絼倓偺砑拥膱D像以及內(nèi)置圖像。選擇 stopped。
制作第二個(gè) stack view:選擇 time remaining label 和 image view。選擇 Editor/Embed In/Stack View?,F(xiàn)在,您需要配置此 stack view 以填充可用空間。單擊 Visual Editor 底部的 Add New Constraints 按鈕,并設(shè)置以下約束:
Stack view 已按需擴(kuò)展,但 image view 仍然太小。選擇 image view,并將其左右約束設(shè)置為 Standard Value,如圖所示。
在 Attributes Inspector 中,將 ** Scaling** 設(shè)置為 Proportionally Up or Down。
構(gòu)建并運(yùn)行 app 。調(diào)整窗口大小以檢查所有 UI 元素是否按預(yù)期調(diào)整大小和位置。
將 UI 連接到 代碼
正如在本系列的第 1 部分中學(xué)到的,你需要設(shè)置 @IBOutlets 和 @IBActions 以將 UI 連接到代碼。在此窗口中,您需要以下元素的@IBOutlets:
- Time remaining label
- Egg image view
- The 3 buttons
3 個(gè)按鈕也需要 @IBActions 以在用戶點(diǎn)擊它們的時(shí)候觸發(fā)函數(shù)。在項(xiàng)目導(dǎo)航器中,選擇 Main.storyboard。按住 Option 單擊 Project Navigator 中的 ViewController.swift 以在 Assistant 編輯器中打開它。如果空間不足,請使用右上角的按鈕隱藏 Utilities 和 Navigator 面板。
選擇 countdown timer label 并按住 Control 拖動(dòng)到 ViewController 類中,就像在第 1 部分中做的那樣。將 label 的名稱設(shè)置為 timeLeftField。對 egg image view 重復(fù)同樣的操作,將其名稱設(shè)置為 eggImageView。設(shè)置按鈕的 outlets,命名為 startButton,stopButton 和 resetButton。
這些按鈕還需要 @IBActions。按住 Control 從 Start 按鈕拖動(dòng),但這次將 Connection 彈出窗口更改為 Action,并將名稱設(shè)置為 startButtonClicked。對其他按鈕重復(fù),創(chuàng)建名為 stopButtonClicked 和 resetButtonClicked 的 action。
如果你像我一樣,忘記更改 Connection 彈窗窗口到 Action,你最終將有兩個(gè) @IBOutlets 而沒有 @IBAction。要?jiǎng)h除額外的 @IBOutlet,首先刪除 ViewController 中多余的代碼行。然后在 Utilities 中打開 Connections Inspector。
你會(huì)看到 Referencing Outlets 下的兩個(gè)條目。點(diǎn)擊錯(cuò)誤的旁邊的 X 將其刪除。然后回去,記得改為 Connection 彈出窗口以使用 @IBAction。
ViewController 代碼現(xiàn)在應(yīng)該如下所示:
在本系列的第 3 部分中,將向這些函數(shù)添加代碼以使它們工作。現(xiàn)在關(guān)閉 Assistant Editor ,重新打開 Navigator 和 Utilities 面板(如果你把它們關(guān)上了的話。
菜單
在 Main.storyboard 中,單擊 menu bar 或 Application Scene 以將其選中。app 模板提供了一組默認(rèn)的菜單,但對于此 app,大多數(shù)菜單不必要。瀏覽菜單最簡單的方法是使用 Document Outline。使用三角形展開顯示 View 菜單及其內(nèi)容。
菜單欄的結(jié)構(gòu)是一系列嵌套菜單和菜單項(xiàng)。切換到 Utilities 面板中的 Identity Inspector,以便在單擊列表時(shí)可以看到列表中的每個(gè)條目。Main Menu 是 NSMenu 類的一個(gè)實(shí)例。它包含一個(gè) NSMenuItems 數(shù)組:View 是其中之一。
View 菜單項(xiàng)包含一個(gè)帶有自己的 NSMenuItems 的子菜單(NSMenu)。注意 Separator 項(xiàng),它只是 NSMenuItem 的一種特殊形式。
首先要做的是刪除這個(gè) app 不需要的菜單。選擇 Document Outline 中的 File 菜單,然后按 Delete 將其刪除。如果在可視編輯器中選擇它并刪除,將只刪除了 File 菜單項(xiàng)中的菜單,因此會(huì)在菜單欄中留下一個(gè)空白。如果發(fā)生這種情況,選擇那個(gè)空白,然后再次按 Delete 將其刪除。
繼續(xù)刪除菜單,直到只剩 EggTimer,Window 和 Help。
現(xiàn)在你要添加一個(gè)新的菜單,模擬 3 個(gè)按鈕的操作。在 Object Library 中搜索 “menu”。記住每個(gè)菜單以 menu item 開始,將 Menu Item 拖動(dòng)到 EggTimer 和 Window 之間的菜單欄。它將顯示為一個(gè)藍(lán)色框,但這是因?yàn)樗鼪]有標(biāo)題。
現(xiàn)在將 Menu 拖動(dòng)到藍(lán)色框中。如果你發(fā)現(xiàn)難以定位到藍(lán)色框,請拖動(dòng)到 Document Outline ,就在新的 Item 下面。新菜單仍然沒有標(biāo)題,但它現(xiàn)在有三個(gè)項(xiàng)目。
選擇菜單(而不是 item),切換到 Attributes Inspector,把 title 改為 Timer。這將為你的新菜單分配一個(gè)名稱。選擇 Item 1,然后通過雙擊編輯或使用 Attributes Inspector 將其標(biāo)題更改為 Start。
單擊 Attributes Inspector 中的 Key Equivalent 字段,然后按 Command-S 分配一個(gè)快捷鍵。通常 Command-S 意味著保存,但是因?yàn)椤拔募辈藛我呀?jīng)被刪除了,這就不是沖突了,盡管為其他功能重用常規(guī)快捷鍵并不是一個(gè)好的做法。
使用相同的方法將第二個(gè)項(xiàng)目的標(biāo)題設(shè)置為 Stop,使用快捷鍵 Command-X,將第三個(gè)項(xiàng)目的標(biāo)題設(shè)置為 Reset,使用快捷鍵 Command-R。
可以在可視化編輯器中的菜單欄頂部看到三個(gè)按鈕。切換到 Identity Inspector。依次點(diǎn)擊每一個(gè),顯示它們都鏈接到了 Application 、 First Responder 和 AppDelegate。第一響應(yīng)者通常是最前面的視圖控制器,并且它可以從菜單項(xiàng)接收動(dòng)作。
按住 Option 單擊 ViewController.swift,并在按鈕的 @IBActions 下面添加以下代碼:
// MARK: - IBActions - menus
@IBAction func startTimerMenuItemSelected(_ sender: Any) {
startButtonClicked(sender)
}
@IBAction func stopTimerMenuItemSelected(_ sender: Any) {
stopButtonClicked(sender)
}
@IBAction func resetTimerMenuItemSelected(_ sender: Any) {
resetButtonClicked(sender)
}
這些函數(shù)將被菜單調(diào)用,并且它們將調(diào)用按鈕動(dòng)作函數(shù)。你可以有直接調(diào)用按鈕動(dòng)作函數(shù)的菜單項(xiàng),但我沒有選擇這樣做的目的是使事件序列在調(diào)試時(shí)更加明顯。保存文件并關(guān)閉 Assistant Editor。
按住 Control 從 Start 菜單項(xiàng)向上拖動(dòng)到指示 First Responder 的橙色塊。彈出窗口將顯示一個(gè)數(shù)量繁多的選項(xiàng)列表。輸入 “sta” 會(huì)快速滾動(dòng)到正確的部分,并選擇 startTimerMenuItemSelected。
用同樣的方式將 Stop 和 Reset 菜單項(xiàng)連接到 stopTimerMenuItemSelected?,F(xiàn)在當(dāng) EggTimer 窗口在前面時(shí),選擇菜單項(xiàng)將調(diào)用這些函數(shù)。
然而,3 個(gè)按鈕不是全部被同時(shí)啟用,并且菜單項(xiàng)需要反映按鈕的狀態(tài)。這不會(huì)發(fā)生在 ViewController 中,因?yàn)樗粫?huì)總是 First Responder,因此將在 AppDelegate 中控制菜單項(xiàng)。
打開 Main.storyboard 并顯示菜單,按住 option 在 Project Navigator 中點(diǎn)擊 AppDelegate.swift。按住 Control 從 Start 菜單拖動(dòng)到 AppDelegate,并分配一個(gè) outlet 名稱 startTimerMenuItem。
在第 3 部分中,將添加代碼以根據(jù)需要啟用和禁用這些菜單項(xiàng),但是現(xiàn)在,需要關(guān)閉自動(dòng)啟用和禁用。通常,app 會(huì)檢查當(dāng)前的 First Responder 是否具有菜單項(xiàng)的操作,如果沒有就禁用它。對于這個(gè) app,需要自己控制。選擇 Timer 菜單,并取消選中 Attributes Inspector 中的 Auto Enables Items。
偏好設(shè)置窗口
EggTimer app 的主窗口現(xiàn)在看起來不錯(cuò),但它需要一個(gè)偏好設(shè)置窗口,以便用戶可以選擇煮雞蛋的程度。
“偏好設(shè)置”將顯示在具有自己的窗口控制器的單獨(dú)窗口中。這是因?yàn)椤捌迷O(shè)置”窗口將有不同的默認(rèn)大小,并且不能調(diào)整大小??梢杂赏淮翱诳刂破黠@示多個(gè)視圖控制器,但是它們將共享該窗口控制器的屬性。
打開 Main.storyboard,關(guān)閉 Assistant Editor(如果開著的話),并在 Objects Library 中搜索 “window”。將一個(gè)新的窗口控制器拖動(dòng)到可視化編輯器中。它也將創(chuàng)建一個(gè)視圖控制器以顯示其內(nèi)容。將它們排列在窗口中,讓新的窗口控制器靠近菜單欄,以便它們易于查看。
打開 EggTimer 菜單,并按住 Control 從 Preferences.. 拖動(dòng)到新的窗口控制器。從出現(xiàn)的彈出窗口中選擇 Show。這將創(chuàng)建一個(gè) segue,以便每當(dāng)用戶從 EggTimer 菜單中選擇 Preferences… 時(shí),此窗口控制器將顯示新的視圖控制器。
Preferences 窗口控制器將顯示一個(gè)新的視圖控制器,因此現(xiàn)在需要為該視圖控制器創(chuàng)建類。在 Project Navigator 中,選擇現(xiàn)有的 ViewController.swift 文件;這確保新文件將放在 Project Navigator 中有邏輯的位置。選擇 File/New/File…
選擇 macOS/Cocoa Class 然后點(diǎn)擊 Next。將類名設(shè)置為 PrefsViewController,并使其成為 NSViewController 的子類。檢查語言是否設(shè)置為 Swift,并取消選中 Also create XIB file for user interface。單擊 Next 和 Create 以保存文件。
返回 Main.storyboard,選擇新的視圖控制器。確保選擇視圖控制器本身,而不是它的視圖;使用 Document Outline 會(huì)更容易。在 Identity Inspector 中,將其類設(shè)置為 PrefsViewController。
在偏好設(shè)置窗口控制器中選擇窗口,并使用 Attributes Inspector 將其標(biāo)題設(shè)置為 Preferences。不要設(shè)置自動(dòng)保存名稱,因?yàn)榇舜翱诿看味紩?huì)出現(xiàn)在屏幕中央。取消選中 Minimize 和 Resize 控件,以使窗口大小固定。
轉(zhuǎn)到 Size Inspector,并為內(nèi)容大小輸入寬度 416 和高度 214。 在 Initial Position 下,從 2 個(gè)彈出窗口中選擇 Center Horizontally 和 Center Vertically。
選擇 PrefsViewController 中的 View,并使用 Size Inspector 將其寬度更改為 416,將高度更改為 214。
PrefsViewController 將顯示一個(gè)彈出窗口,用于選擇預(yù)設(shè)時(shí)間,以及用于選擇自定義時(shí)間的滑塊。這兩個(gè)每個(gè)會(huì)有一個(gè) label,還有兩個(gè)按鈕:Cancel 和 OK。還會(huì)有一個(gè)動(dòng)態(tài) label 顯示當(dāng)前選擇的時(shí)間。
將一下控件拖到視圖控制器中,如下排列它們:
- Label——設(shè)置 title 為 “Preset Egg Timings:”
- Pop Up Button
- Label——設(shè)置 title 為 “Custom Egg Timing:”
- Label——設(shè)置 title 為 “6 minutes”
- Horizontal Slider
- Push Button——設(shè)置 title 為 “Cancel”
- Push Button——設(shè)置 title 為 “OK”
由于此窗口不會(huì)調(diào)整大小,因此不需要應(yīng)用任何自動(dòng)布局約束——對象將始終像安排的那樣顯示。拖動(dòng)對象安排它們的位置,藍(lán)色指導(dǎo)線會(huì)幫助你。將 “6 minutes” label 的寬度擴(kuò)展到窗口右側(cè)附近,因?yàn)樗赡馨辔谋?。雙擊 Pop Up Button 查看前三個(gè)項(xiàng)目并將其標(biāo)題設(shè)置為:
- For runny soft-boiled eggs (barely set whites): 3 minutes
- For slightly runny soft-boiled eggs: 4 minutes
- For custardy yet firm soft-boiled eggs: 6 minutes
從 Objects Library 中拖出兩個(gè) Menu Item,還有一個(gè) Separator Menu Item,最后是另一個(gè) Menu Item。如果您在放置時(shí)遇到任何問題,請使用 Document Outline。
把剩下的 menu item 標(biāo)題設(shè)置為:
- For firm yet still creamy hard-boiled eggs: 10 minutes
- For very firm hard-boiled eggs: 15 minutes
- Custom
我不會(huì)假裝自己是煮雞蛋專家,所以我從 The Kitchn 那里得到了這些時(shí)間以及描述。
選擇 popup 本身,而不是任何 item,并將其 Selected Item 設(shè)置為 6 minute 選項(xiàng)。
現(xiàn)在要做一個(gè)小竅門,讓 app 準(zhǔn)確知道選擇的分鐘數(shù)。對于 popup 中的每個(gè) menu item,在 Attributes Inspector 中將其 tag 設(shè)置為分鐘數(shù):3,4,6,10,15。Custom menu item 的 tag 保留為0。
現(xiàn)在選擇 Slider,并在 Attributes Inspector 中將 Tick marks 設(shè)置為 25,Minimum Value 設(shè)置為 1,Maximum Value 設(shè)置為25,Current Value 設(shè)置為6,并選中 Only stop on tick marks。看見刻度標(biāo)記后,你可能需要將滑塊向下移動(dòng)幾像素。通過取消選中 Enabled 來禁用滑塊——只有在彈出窗口中選擇了 Custom 時(shí),才會(huì)啟用。
連接 Preferences 的對象
按住 option 從 Project Navigator 中單擊 PrefsViewController.swift,如果需要更多空間的話,隱藏側(cè)邊欄。popup 需要 slider 和顯示 “6 minutes” 的 label 的 @IBOutlets。按住 Control 把它們拖到 PrefsViewController 里,outlet 命名如下:
- Popup:
presetsPopup - Slider:
customSlider - Label:
customTextField
接下來,按住 Control 以創(chuàng)建 @IBActions,記得每次在彈出窗口里都要將 Action 設(shè)置為 Connection:
- Popup:
popupValueChanged - Slider:
sliderValueChanged - Cancel button:
cancelButtonClicked - OK button:
okButtonClicked
代碼現(xiàn)在應(yīng)該如下所示:
“Preferences” 窗口的布局現(xiàn)已完成。構(gòu)建并運(yùn)行 app,并從 EggTimer 菜單中選擇 Preferences??赐旰髥螕魳?biāo)題欄按鈕中的紅色關(guān)閉按鈕以關(guān)閉窗口。
App Icon
現(xiàn)在 UI 中只剩為 app 添加圖標(biāo)了。你已下載 app 的資源文件夾,并安裝了一些圖像到 Assets.xcassets 中。再次打開它,找到 egg-icon.png 文件。
從 Project Navigator 中選擇 Assests.xcassets,點(diǎn)擊 AppIcon 并將 egg-icon.png 拖動(dòng)到 Mac 256pt 1x 框中。如第 1 部分所述,如果要發(fā)布 app,需要提供 AppIcon 中顯示的所有尺寸,但對于此 app,單個(gè)大小就足夠了。
構(gòu)建并運(yùn)行 app,確認(rèn) Dock 中有新圖標(biāo)。如果看到的仍然是默認(rèn)圖標(biāo),請從 Xcode 的 Product 菜單中選擇 Clean,然后重試。
下一步?
現(xiàn)在,app 的 UI 已經(jīng)完全實(shí)現(xiàn)了,但它還不能做任何事。如果在某個(gè)地方?jīng)]有跟上,可以 下載 Xcode 項(xiàng)目 ,其中所有的 UI 都已為下一部分準(zhǔn)備好。
在本系列教程的第 3 部分中,會(huì)添加代碼以使 app 正常工作。
如果對本教程有任何問題或意見,請?jiān)谙路皆u(píng)論!