[譯] 零基礎 macOS 應用開發(二)
本文翻譯自 raywenderlich.com 的 macOS 開發經典入門教程 ,已咨詢對方網站,可至多翻譯 10 篇文章。
還是 先打賞 然后去閱讀英文原吧,畢竟無論是 Xcode,抑或是官方的文檔,還是各種最前沿的資訊都只有英文版本。
綜上,此翻譯版本僅供參考,謝絕轉載。
相關鏈接
零基礎 macOS 應用開發(一): 原文 / 譯文
零基礎 macOS 應用開發(二): 原文 / 譯文(本文)
零基礎 macOS 應用開發(三): 原文 / 譯文
歡迎回到我們的零基礎 macOS 應用開發教程的第二部分(共三部分)!
在第一部分(原文 / 譯文),你了解了如何安裝 Xcode、如何創建一個新的 app、添加 UI、連接代碼與 UI、運行 app、調試 app,以及尋求幫助。如果你對上述內容還有任何不確定之處,再回去瀏覽一遍第一部分吧。
在這一部分,你將會創建一個界面更加復雜的 app。你將會學到如何應對可調整大小的窗口,以及設計第二個頁面——偏好設置頁面,并讓你的 app 能跳轉到這個新創建的頁面。
準備開始
和第一部分一樣,打開 Xcode 并在歡迎頁面點擊 Create a new Xcode project,或選擇 File 菜單中的 New Project…。接下來在 macOS 下的 Application 標簽頁中選擇 Cocoa Application,并點擊 Next,把你的 app 命名為 EggTimer,確保 Language 選項為 Swift 以及 Use Storyboards 被選中。點擊 Next 然后找一個合適的地方存儲這個項目。
編譯并運行你的 app 來確保一切正常。
EggTimer App
你將會開發的 app 叫做 EggTimer,它能幫助用戶倒計時,并顯示剩余的時間。App 的界面上會有一個雞蛋的圖標,隨著時間的臨近,雞蛋會被慢慢煮熟;當你的雞蛋煮熟時,還會有一個提示音。App 內還有一個頁面會顯示此 app 的偏好設置。
在 Project Navigator(項目導航器)中打開 Main.storyboard,正如第一部分所說的,你已經擁有了三個場景:
- Application Scene(應用場景)包含了只要 app 運行著就會顯示的菜單欄。
- Window Controller(窗口控制器)是定義了 app 窗口有怎樣行為(怎么調整大小、窗口怎么出現、app 是否會記住上次調整的大小和位置等等)的部分;其實一個 Window Controller 可以管理多個窗口,但如果它們的屬性不同,你就需要多個 Windows Controller 了。
- View Controller 則顯示了窗口中的用戶界面——也就是你對 UI 進行布局的地方。
你注意到了嗎?有一個箭頭指向了 Window Controller,這表明它是 app 啟動時的 initial display(初始頁面)。你可以在 Document Outline(文檔大綱)中選擇 Window Controller,然后在 Attributes Inspector(屬性檢查器)中查看這個設置。取消勾選 Is Initial Controller(用作首要控制器)后,箭頭就消失了。因為你需要它來作為首要的控制器,請把這個選項勾選上。
Window Controller(窗口控制器)
在你開始著手 UI 前,請確保你已經在 Project Navigator(項目導航器)中選擇了 Main.storyboard。點擊來選擇其中的 window(窗口)。在可視化編輯器中,Window Controller顯示了「View Controller」這行字,因為它包含著一個 View Controller(視圖控制器)。在這個 app 中,我們不希望用戶將窗口調整到 346 × 471 像素以下,這兩個數值也將會是 app 啟動時窗口的默認大小。
在 Utilities(工具集)面板中前往 Size Inspector(尺寸檢查器),設置 Content Size Width(內容寬度)和 Content Size Height(內容高度)分別為 346;勾選 Minimum Content Size(最小內容尺寸)的復選框,并確保其數值與內容尺寸相同?,F在,在可視化編輯器里的 Window Controller 的尺寸應該已經發生了變化,因此可能會蓋住其他元素,你可能需要重新排列一下它們。
雖然不是必須的,但當你把 View Controller 和承放它的 Window Controller 調整到一樣的大小后,后續操作會直觀很多。點擊并選中 View Controller,在 Size Inspector(尺寸檢查器)中設置 Width 和 Height 分別為 346 和 471。如果需要的話,重新排列一下界面上的各個元素防止它們重疊。現在可視化編輯器里的 Window Controller 和 View Controller 都有著一樣的尺寸了。
選中 WindowController 里的 Window,在 Attributes Inspector(屬性檢查器)中把它的名字更改為 Egg Timer,設置 Autosave name 為 EggTimerMainWindow,這樣用戶上次調整的窗口的尺寸和大小在 app 下次啟動時就都會被記住了。
如果你是一個 iOS 開發者,你應該已經處理過不同設備、屏幕尺寸、屏幕旋轉方向下的適配問題了。在 macOS 開發過程中,你必須處理無限多種窗口尺寸和長寬比,因此我在這里把窗口的默認尺寸調整成了這么一個奇怪的數字。不過好消息是,Auto Layout 幫助你解決了這個問題。
布局 UI(一)
基本的 UI 包含了兩個 Stack View(堆疊視圖):第一部分包含顯示剩余時間的 Label 和雞蛋的圖標。第二個則在底部包含三個按鈕,我們先來制作按鈕:
- 在 Object Library(控件庫)中搜索「Button」
- 向 View Controller 中拖動一個 Gradient button
- 使用 Attributes Inspector(屬性檢查器)刪除它的圖像,并把它 Title 設置為 Start
-
把字體改成 System 24
- 把按鈕拖大一些讓文字能顯示完全
- 選中 Start 按鈕,按下 Command? + D 兩次來復制兩份
- 把新復制的按鈕向右拖動一些,并把他們的 Title 改成 Stop 和 Reset
- 同時選中三個按鈕,然后點擊菜單欄上的 Editor → Embed In → Stack View
為了讓按鈕填滿整個 Stack View,選擇新創建的 Stack View,然后在 Attributes Inspector(屬性檢查器)里做出如下更改:
- Distribution(分配): Fill Equally(均分填滿)
- Spacing(間距): 0
在可視化編輯器的底部點擊 Add New Constraints(添加新的約束條件),設置左邊、右邊、底部和高度如上圖所示。將 Update Frames 設置為 Items of New Constraints,然后點擊 Apply 4 Constraints(應用四條約束)。
Stack View 現在已經放置好了,但這些按鈕似乎比 Stack View 矮了一些,在 Document Outline(文檔大綱) 中,按住 Control? 鍵的同時拖動 Start 按鈕到 Stack View 上,并選擇 Equal Heights(高度等同)。對另外兩個按鈕進行同樣的操作。
現在,包含了按鈕的 Stack View 已經完全是我們想要的樣子了。
編譯并運行你的 app,嘗試著改變窗口的大小,這些按鈕會緊緊地貼在窗口的底部,并自動填滿整個窗口的寬度。
最后一步,在 Attributes Inspector(屬性檢查器)中取消選中 Enabled 來禁用 Stop 按鈕和 Reset 按鈕,在計時開始前,這兩個按鈕不應該被啟用。
布局 UI(二)
第二個 Stack View 包含剩余的時間以及雞蛋的圖片。向 View Controller 拖入一個 Label,把它的 Title(標題)設置為 6:00 并把 Alignment(對齊)設置為 center(居中)。默認的字體(San Francisco)會自動調整字符的間距——這意味著:隨著倒計時的時間的變化,數字會來回跳躍——這會很煩人。
把字體改成 Helvetica Neue 來避免這樣,然后把 Size(字體大?。┰O置為 100。這會使文字超出 Label 的范圍,所以調整 Label 的大小直到你可以看到完整的文字。
現在我們需要添加一張圖片,在 Object Library(控件庫)的搜索框中輸入「image」,這會過濾出很多長得很像的控件,你需要的是 Image View,將它拖動到 View Controller 中剩余時間 Label 的下方。
下載這個工程需要的資源文件(一些圖片和一個聲音文件),解壓碎并打開 Egg Images 文件夾。回到 Xcode,在 Project Navigator(項目導航器)中點擊 Assets.xcassets。
把這六個圖片拖動到 Assets Library(資產庫)中,然后它們就可以在你的 app 中使用了。因為圖片的文件名中帶有「@2x」,它們會被自動分配到各個圖像資產的 @2x 區域。
關于 @2x 請自行搜索 Mac/iOS 的 HiDPI適應,譯者注
前往 Main.storyboard,選中你剛剛添加的 Image View,在 Attributes Inspector(屬性檢查器)中點擊 Image(圖像)下拉框,在這個下拉框中你可以看到很多內置的圖片以及你剛剛添加的圖片,選中 stopped。
現在我們來制作第二個 Stack View:選中顯示剩余時間的 Label 和剛剛調整好的 Image View,在菜單欄上點擊 Editor → Embed In → Stack View。然后我們來調整一下這個 Stack View,使它填滿剩余的空間。點擊可視化編輯器底部的 Add New Constrains(添加新的約束),然后添加下圖所示的約束條件:
正我們所需要的,Stack View 填滿了剩下的空間,但這個 Image View 太小了,選中它,將它左右兩您的約束按照下圖設置為 User Standard Value(使用標準值):
在 Attributes Inspector(屬性檢查器)中,設置 Scaling(縮放) 為 Proportionally Up or Down(等比放大或縮?。?/strong>。
編譯并運行 app,調整窗口的大小,看看 UI 元素是否如預期地自動地適應了窗口尺寸。
連接 UI 和代碼
在第一部分(原文 / 譯文)中,你已經知道了你需要使用 @IBOutlets
和 @IBActions
來連接你的 UI 和代碼。在這個例子中,你需要為以下元素設置 @IBOutlets
:
- 剩余時間的 Label
- 顯示雞蛋的 Image View
- 三個按鈕
此外,這三個按鈕還需要@IBAction
來響應用戶的點擊操作。在 Project Navigator(項目導航器)中,先選中 Main.storyboard,再按住 Option?,點擊 ViewController.swift,這樣就可以在 Assistant Editor(輔助編輯器,即左右分欄)中打開它了。如果你的屏幕空間不足,點擊右上角的按鈕來隱藏 Utilities(工具集) 和 Navigator(導航器) 面板。
和第一部分一樣,選中剩余時間的 Label,按住 Control? 鍵的同時拖動它到 ViewController
類中,將名稱設置為 timeLeftField
,對雞蛋的 Image View 進行一樣的操作,它的名字設置為 eggImageView
。然后是三個按鈕,他們的名字分別設置成 startButton
、stopButton
和 resetButton
。
這三個按鈕還需要 @IBAction
,按住 Control? 并拖動 Start 按鈕到代碼中,但這次在彈出窗口中將 Connection 設置為 Action,并把它的名字設置為 startButtonClicked
,對另外兩個按鈕重復以上動作,為它們添加名為 stopButtonClicked
和 resetButtonClicked
的 @IBAction。
如果你和我一樣也經常忘了把 Connection 修改為 Action 的話,你會得到 @IBOutlets
而不是 @IBAction
,如果你要刪除這些多余的 @IBOutlet
,先把這一行代碼刪除,再轉到 Utilities(工具集)里的 Connections Inspector(連接檢查器),你會看到 Referencing Outlets 部分里有兩個項目,點擊錯誤連接旁的 × 來刪除它,然后重新連接你的 @IBAction
(這一次別忘了修改 Action 哦??)。
ViewController
里的代碼現在看起來應該像這樣:
在第三部分中,你將會給這些動作添加代碼,現在我們關閉 Assistant Editor(輔助編輯器),如果你剛剛隱藏了 Navigator(導航器) 和 Utilities(工具集),重新打開它們。
菜單
在 Main.storyboard,在 Application Scene(應用場景) 中點擊 menu bar(菜單欄),Xcode 提供的模版已經內置了一些默認的按鈕,但是在這個 app 中,很多按鈕都派不上用場,最簡單的辦法來瀏覽菜單里的項目是使用 Document Outline(文檔大綱),點擊左側的小三角來顯示 View 菜單和里邊的內容。
菜單欄的結構是由一些 Menus(菜單)和 Menu Items(菜單項目)組成的,切換到 Utilities(工具集)里的 Identity Inspector(身份檢查器),這樣你就能看到,當你點擊各個菜單項目的時候,真正觸發的東西是什么了。Main Menu 是 NSMenu
的實例,它包含了 NSMenuItem
組成的數組:View 菜單就是這個數組的一個成員。
View 菜單包含一個子菜單(NSMenu
),里面包含了它自己的 NSMenuItems
,值得注意的是,Separator
(分割線)是 NSMenuItem
的特殊形式。
我們要做的第一件事是刪除這個 app 里不需要的菜單項,在 Document Outline(文檔大綱)中選中 File 菜單,按下鍵盤上的 Delete? 鍵把它刪掉。如果你是在 Visual Editor(可視化編輯器)中選中它并刪除的話,你會把 File 菜單里的菜單項都刪掉,然后只剩下一個空空如也的菜單,如果你不小心這樣做了,選中那個空白然后再次按下 Delete? 來移除它。
這段話的后半段沒有看太明白,望各位指正,原文是: If you select it in the Visual Editor and delete, you will only have deleted the menu inside the File menu item, so you will be left with a space in the menu bar. If this happens, select the space and press Delete again to remove it.
繼續刪除其他的 Menu,直到你只剩下了 EggTimer、Window 和 Help 菜單。
現在你需要添加一個新的菜單來模擬三個按鈕的操作,在 Object Library(控件庫) 中搜索 「menu」,請注意,菜單欄中的每一個項目(如 File、Help )都是 Menu Item,點擊它之后打開的才是 Menu,拖動一個 Menu Item 到菜單欄中 Egg Timer 和 Window 之間的地方,它會顯示成一個藍色的方框,這是因為它內部還沒有一個帶有標題的 Menu。
現在想那個藍色的方框拖入一個 Menu,如果你覺得那個方框太小拖不準的話,你也可以拖到 Document Outline(文檔大綱)里我們剛剛創建的 Item 的下方。新的 Menu 還沒有標題,但他已經有了三個菜單項。
選中 Menu(不是 item),切換到 Attributes Inspector(屬性檢查器)并把 Title(標題)設置為 Timer,選中 Item 1,雙擊它(或在屬性檢查器里)把他的標題改為 Start。
在 Attributes Inspector(屬性檢查器)中點擊 Key Equivalent(快捷鍵),然后按下 Command? + S 來設置快捷鍵。通常情況下 Command? + S 是保存的快捷鍵,但是你已經刪除了 File 菜單,這樣的設置并不會有沖突,但在一般情況下我們還是不要復用常見快捷鍵的比較好。
用同樣的方法把第二、三個 item 的標題分別設置為設置為 Stop 和 Reset,快捷鍵分別為 Command? + X 和 Command? + R。
在可視化編輯器中 Menu Bar 的上方,你能看到三個按鈕,切換到 Identity Inspector(身份檢查器),然后依次點擊這三個按鈕,你可以看到,他們分別連接著 Application、First Responder、和 AppDelegate。First Responder 通常就是當前處于最前端的 View Controller,它可以從 Menu Item 那里接收動作。
按住 Option? 的同時點擊 ViewController.swift,然后在你剛剛為按鈕們添加的 @IBAction
的下方添加以下代碼:
// MARK: - IBActions - menus
@IBAction func startTimerMenuItemSelected(_ sender: Any) {_
startButtonClicked(sender)
}
@IBAction func stopTimerMenuItemSelected(_ sender: Any) {
stopButtonClicked(sender)
}
@IBAction func resetTimerMenuItemSelected(_ sender: Any) {
resetButtonClicked(sender)
}
這些方法會在菜單項被點擊時被調用,然后它們會去調用界面上那三個按鈕的 IBAction 方法。其實,你可以讓 Menu Item 直接調用按鈕的方法(即把 Menu Item 的 IBAction 也拖到之前定義的按鈕的 IBAction 上),但在這里我選擇這種方式,因為這樣的話,調試時這一系列事件會更加清晰明了?,F在保存文件并退出 Assistant Editor(協助編輯器)。
按住 Control? 鍵的同時,把 Start 菜單項拖動到象征著 First Responder 的橙色立方體上,一個包含了很多選項的窗口會彈出來,在鍵盤上輸入「sta」來快速滾動到我們所需要的 startTimerMenuItemSelected 并選擇它。
用同樣的方法把 Stop 菜單項和 Reset 菜單項分別連接至 stopTimerMenuItemSelected
和 resetTimerMenuItemSelected
?,F在當 EggTimer 窗口處于最前端時,點擊菜單上的按鈕將會調用這些方法。
但是還有一個問題:這三個按鈕在同一時刻并非同時都是可用的,而且菜單欄上的按鈕需要體現出他們是否可用。這個功能無法在 ViewController
里實現,因為它不會一直處于 First Responder 的狀態,所以我們需要轉戰 AppDelegate
。
打開 Main.storyboard 并確保這幾個 Menu 都處于可見狀態,在 Project Navigator(項目導航器)中按住 Option? 鍵的同時點擊 AppDelegate.swift,按住 Control? 鍵的同時把 Start 菜單項拖動到 AppDelegate
中,創建一個名為 startTimerMenuItem
的 IBOutlet。
用同樣的方法為另外兩個菜單項創建名為 stopTimerMenuItem
的 resetTimerMenuItem
IBOutlet。
在第三部分中你將了解到如何用代碼來根據需要啟用和禁用這些菜單項,但是現在你需要做的是關閉自動啟用與禁用,因為在一般情況下,app 會檢測當前的 First Responder 是否包含了 menu item 所連接的 action(一般就是 IBAction),如果沒有,就會將他們禁用。在這個 app 中,我們希望自己來控制這件事情,所以選中 Timer 菜單項,在 Attributes Inspector(屬性檢查器) 中取消選中 Auto Enables Item(自動啟用項目)。
偏好設置窗口
現在 EggTimer app 的主界面已經看起來很好了,但他還需要一個偏好設置窗口來讓用戶選擇他們想把雞蛋煮多熟。
偏好設置界面將會顯示在一個單獨的窗口中,且擁有它自己的 Window Controller。這是因為盡管在一個 Window Controller 中顯示多個 View Controller 是完全可行的,但它們會共享這個 Window Controller 的所有屬性,而偏好設置窗口的默認大小與主窗口不一樣,而且不可以調整大小。
打開 Main.storyboard,如果 Assistant Editor(輔助編輯器)還處于打開狀態,把它關閉。在 Objects Library(控件庫)中搜索「window」,向 Visual Editor(可視化編輯器)中拖入一個 Window Controller,Xcode 會自動為你創建一個 View Controller 來盛放需要現實的內容。重新排列一下窗口中的內容以便你能更清楚地看清所有內容,并讓你新創建的 Window Controller 更靠近菜單欄。
打開菜單欄上的 EggTimer 菜單,按住 Control? 鍵的同時拖動 Preferences… 菜單項到我們新創建的 Window Controller 上,這將會創建一個 Segue(轉場),也就是說用戶點擊 Preferences… 時,這個 Window Controller 就會把我們新創建的 View Controller 給顯示出來。
偏好設置面板需要顯示一個新的 View Controller,所以你需要為它創建一個新的 View Controller 類。在 Project Navigator(項目導航器)中,選中已經存在了的 ViewController.swift 文件,這會確保新建的文件存儲在與之相同的組中(Xcode 用 Group 來管理文件),然后在 Xcode 的菜單上點擊 File → New → File…。
選擇 macOS → Cocoa Class 然后點擊 Next,設置類名為 PrefsViewController
,父類為 NSViewController
,語言選擇 Swift,不要勾選 Also create XIB file for user interface(同時為 UI 創建 XIB 文件),然后點擊 Next 和 Create 來保存文件。
回到 Main.storyboard,選中我們新創建的 View Controller,請確保你選擇的是 View Controller 而不是它的 View,在 Document Outline(文檔大綱)里選擇會更容易些。在 Identity Inspector(身份檢查器)中,把它的 Class 設置為 PrefsViewController
。
選擇偏好設置窗口里的 Window Controller,前往 Attributes Inspector(屬性檢查器)把它的標題設置為 ** Preferences**。Autosave Name 保持留空,這樣每次窗口出現的時候都會出現在桌面的中央。取消選中 Minimize 和 Resize 復選框,這樣窗口的大小就是固定的了。
前往 Size Inspector(屬性檢查器),把 Content Size 設置為寬 416 高 214。在 Initial Position(默認位置) 下方的兩個下拉框中分別選擇 Center Horizontally 和 Center Vertically。
選中 View 中的 PrefsViewController,在 Size Inspector(尺寸檢查器)中把它的寬和高分別設置為 416 和 214。
PrefsViewController
需要顯示一個用來選擇時間的下拉框和一個用來選擇時間的滑塊,它們都包含各自的 Label 用于顯示標題,還有兩個按鈕:Cancel 和 OK,以及一個用于顯示當前選擇時間的 Label。
拖動以下控件到 View Controller 中并按下表設置它們的屬性:
Label | 設置 title 為「Preset Egg Timings:」 |
Pop Up Button | |
Label | 設置 title 為「Custom Egg Timing:」 |
Label | 設置 title 為 「6 minutes」 |
Horizontal Slider | |
Push Button | 設置 title 為 「Cancel」 |
Push Button | 設置 title 為「OK」 |
因為這個窗口不能調整大小,控件們會按照你的布局進行顯示,所以我們不需要給它設置 Auto-layout 約束條件。把界面上的元素排列好,Xcode 自動會用藍色的參考線來幫助你進行對齊。將顯示「6 minutes」的 Label 的右邊拖動到幾乎與邊界持平,因為它可能需要顯示更多內容。雙擊 Pop Up Button,你會看到三個子項,把他們的標題分別設置為:
- 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(控件庫)**中拖動兩個 Menu Item、一個 Separator Menu Item 和另一個 Menu Item 到下拉框中,如果你覺得直接拖動它們到界面上有點難度的話,也可以拖動到 Document Outline(文檔大綱)的相應位置。
給剛剛拖入的三個子項分別設置標題:
- For firm yet still creamy hard-boiled eggs: 10 minutes
- For very firm hard-boiled eggs: 15 minutes
- Custom
我其實一點都不知道怎么煮雞蛋,所以我從 The Kitchn 找到了以上的時間和描述。
選中下拉框本體(而不是那幾個子項),把它的 Selected Item(已選中項目)設置為那個「6 minute」的選項。
現在你需要讓你的 app 知道用戶在下拉框中到底選擇了哪個子項,依次選中下拉菜單中的每一個自子項,在 Attributes Inspector(屬性檢查器)中設置它們的 Tag 分別為對應的分鐘數:3、4、6、10、15(Custom 子項設置為 0)。
現在選擇 Slider,在 Attributes Inspector(屬性檢查器)中將 Tick marks(刻度數) 設置為 25,Minimum Valve 設置為 1,Maximum Valve 設置為 25,并勾選 Only stop on tick marks(只能選擇刻度),你需要將滑塊向下移動一些來適應新出現的刻度。因為只有當用戶在下拉框種選擇了 Custom 的時候才會啟用,所以我們還需要取消選擇 Enabled。
連接偏好設置里的元素和代碼
在 Project Navigator(項目導航器)中按住 Option? 鍵的同時點擊 PrefsViewController,如果你需要更多空間的話隱藏掉側邊面板。你需要為下拉框、滑動條和顯示“6 minutes”的 Label 添加 @IBOutlet
。把它們依次拖動到 PrefsViewController.swift
中,并給這些 IBOutlet 分別起名:
- Popup:
presetsPopup
- Slider:
customSlider
- Label:
customTextField
接下來,按住 Control? 鍵的同時拖動以下項目來創建 @IBAction
(別忘了把 Connection 改成 Action 哦~):
- Popup:
popupValueChanged
- Slider:
sliderValueChanged
- Cancel button:
cancelButtonClicked
- OK button:
okButtonClicked
現在你的代碼看起來應該像這樣:
至此,偏好設置面板窗口的布局就已經完成了,編譯并運行你的 app 并在 EggTimer 菜單中選擇 Preferences 菜單,檢查一下打開的偏好設置窗口,然后點擊紅色的關閉按鈕來關閉這個窗口。
App 圖標
UI 部分還剩一步:為你的 app 添加圖標。你之前已經下載了一個包含了這個 app 所需要的所有文件的資產文件夾,而且已經添加了一些圖片到 Assets.xcassets 中,現在我們需要打開這個文件夾并找到 egg-icon.png 文件。
在 Project Navigator(項目導航器)中選擇 Assests.xcassets,點擊 AppIcon 并拖動 egg-icon.png 到 Mac 256pt 1x 的方框中。正如第一部分所說的,在真正的產品中你需要提供所有尺寸的圖標,但在這個 app 中,一個圖標就足夠了。
編譯并運行你的 app,看看新的圖標有沒有出現在 Dock 中,如果沒有,你可能需要在 Xcode 的 Product 菜單中點擊 Clean,然后再試一次。