本文翻譯自 raywenderlich.com 的 macOS 開發經典入門教程,已諮詢對方網站,可至多翻譯 10 篇文章。
翻譯它只是由於宿舍太吵太熱,只有這樣才能一句一句看完,並做爲本身的筆記,但願各位有英語閱讀能力的話,仍是去閱讀英文原吧,畢竟不管是 Xcode,抑或是官方的文檔,仍是各類最前沿的資訊都只有英文版本。
綜上,此翻譯版本僅供參考,謝絕轉載。 前端相關連接
零基礎 macOS 應用開發(一): 原文 / 譯文
零基礎 macOS 應用開發(二): 原文 / 譯文(本文)
零基礎 macOS 應用開發(三): 原文 / 譯文macos
歡迎回到咱們的零基礎 macOS 應用開發教程的第二部分(共三部分)!
在第一部分,你瞭解瞭如何安裝 Xcode、如何建立一個新的 app、添加 UI、鏈接代碼與 UI、運行 app、調試 app,以及尋求幫助。若是你對上述內容還有任何不肯定之處,再回去瀏覽一遍第一部分吧。
在這一部分,你將會建立一個界面更加複雜的 app。你將會學到如何應對可調整大小的窗口,以及設計第二個頁面——偏好設置頁面,並讓你的 app 能跳轉到這個新建立的頁面。swift
和第一部分同樣,打開 Xcode 並在歡迎頁面點擊 Create a new Xcode project,或選擇 File 菜單中的 New Project…。接下來在 macOS 下的 Application 標籤頁中選擇 Cocoa Application,並點擊 Next,把你的 app 命名爲 EggTimer,確保 Language 選項爲 Swift 以及 Use Storyboards 被選中。點擊 Next 而後找一個合適的地方存儲這個項目。
編譯並運行你的 app 來確保一切正常。segmentfault
你將會開發的 app 叫作 EggTimer,它能幫助用戶倒計時,並顯示剩餘的時間。App 的界面上會有一個雞蛋的圖標,隨着時間的臨近,雞蛋會被慢慢煮熟;當你的雞蛋煮熟時,還會有一個提示音。App 內還有一個頁面會顯示此 app 的偏好設置。數組
在 Project Navigator(項目導航器)中打開 Main.storyboard,正如第一部分所說的,你已經擁有了三個場景:app
你注意到了嗎?有一個箭頭指向了 Window Controller,這代表它是 app 啓動時的 initial display(初始頁面)。你能夠在 Document Outline(文檔大綱)中選擇 Window Controller,而後在 Attributes Inspector(屬性檢查器)中查看這個設置。取消勾選 Is Initial Controller(用做首要控制器)後,箭頭就消失了。由於你須要它來做爲首要的控制器,請把這個選項勾選上。less
在你開始着手 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(最小內容尺寸)的複選框,並確保其數值與內容尺寸相同。如今,在可視化編輯器裏的 Window Controller 的尺寸應該已經發生了變化,所以可能會蓋住其餘元素,你可能須要從新排列一下它們。ide
雖然不是必須的,但當你把 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 包含了兩個 Stack View(堆疊視圖):第一部分包含顯示剩餘時間的 Label 和雞蛋的圖標。第二個則在底部包含三個按鈕,咱們先來製做按鈕:
爲了讓按鈕填滿整個 Stack View,選擇新建立的 Stack View,而後在 Attributes Inspector(屬性檢查器)裏作出以下更改:
在可視化編輯器的底部點擊 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 按鈕,在計時開始前,這兩個按鈕不該該被啓用。
第二個 Stack View 包含剩餘的時間以及雞蛋的圖片。向 View Controller 拖入一個 Label,把它的 Title(標題)設置爲 6:00 並把 Alignment(對齊)設置爲 center(居中)。默認的字體(San Francisco)會自動調整字符的間距——這意味着:隨着倒計時的時間的變化,數字會來回跳躍——這會很煩人。
把字體改爲 Helvetica Neue 來避免這樣,而後把 Size(字體大小)設置爲 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(等比放大或縮小)。
編譯並運行 app,調整窗口的大小,看看 UI 元素是否如預期地自動地適應了窗口尺寸。
在第一部分中,你已經知道了你須要使用 @IBOutlets
和 @IBActions
來鏈接你的 UI 和代碼。在這個例子中,你須要爲如下元素設置 @IBOutlets
:
@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 菜單,這樣的設置並不會有衝突,但在通常狀況下咱們仍是不要複用常見快捷鍵的比較好。
用一樣的方法把第2、三個 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 上),但在這裏我選擇這種方式,由於這樣的話,調試時這一系列事件會更加清晰明瞭。如今保存文件並退出 Assistant Editor(協助編輯器)。
按住 Control⌃ 鍵的同時,把 Start 菜單項拖動到象徵着 First Responder 的橙色立方體上,一個包含了不少選項的窗口會彈出來,在鍵盤上輸入「sta」來快速滾動到咱們所須要的 startTimerMenuItemSelected 並選擇它。
用一樣的方法把 Stop 菜單項和 Reset 菜單項分別鏈接至 stopTimerMenuItemSelected
和 resetTimerMenuItemSelected
。如今當 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,你會看到三個子項,把他們的標題分別設置爲:
再從 Objects Library(控件庫)中拖動兩個 Menu Item、一個 Separator Menu Item 和另外一個 Menu Item 到下拉框中,若是你以爲直接拖動它們到界面上有點難度的話,也能夠拖動到 Document Outline(文檔大綱)的相應位置。
給剛剛拖入的三個子項分別設置標題:
我其實一點都不知道怎麼煮雞蛋,因此我從 The Kitchn 找到了以上的時間和描述。
選中下拉框本體(而不是那幾個子項),把它的 Selected Item(已選中項目)設置爲那個「6 minute」的選項。
如今你須要讓你的 app 知道用戶在下拉框中到底選擇了哪一個子項,依次選中下拉菜單中的每個自子項,在 Attributes Inspector(屬性檢查器)中設置它們的 Tag 分別爲對應的分鐘數:三、四、六、十、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 分別起名:
presetsPopup
customSlider
customTextField
接下來,按住 Control⌃ 鍵的同時拖動如下項目來建立 @IBAction
(別忘了把 Connection 改爲 Action 哦~):
popupValueChanged
sliderValueChanged
cancelButtonClicked
okButtonClicked
如今你的代碼看起來應該像這樣:
至此,偏好設置面板窗口的佈局就已經完成了,編譯並運行你的 app 並在 EggTimer 菜單中選擇 Preferences 菜單,檢查一下打開的偏好設置窗口,而後點擊紅色的關閉按鈕來關閉這個窗口。
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,而後再試一次。