MacOS 軟件開發(一) Hello Word

掘金好像沒有macOS開發專區,所以只好發到iOS專區裏了-_-git

前言

最近由於突然間有一些靈感,想要作一款基於macOS的app。由於以前一直在開發iOS,感受macOS應該也是相似,結果一上手,差點被勸退。所以決定慢慢來,將本身的心得記錄下來。github

正文

這系列文章不會從講述Swift或Objective-c語言的語法細節。所以若是是純小白開發,請先行自學語言。macos

注: 文章系列將會用Swift語言進行所有內容的開發swift

項目的建立

首先咱們須要選擇macOS,選擇 Cocoa App,接下來點擊 Nextapp

create

從storyboard建立項目

storyboard建立工程應該是我最不喜歡的一種方式了,由於以爲故事版這種方式與代碼對應關係很弱,並且它又一樣擁有着xib修改起來費時費力的缺點。ide

  1. 項目的建立與運行

input

在這裏咱們輸入工程名HelloWorldStoryBoard,而後選擇項目存儲地址後。咱們的工程目錄將如這個樣子函數

helloword_storyboard_file

咱們直接按住 Command + R 運行程序,將會有一個窗口展現出來,裏面什麼都沒有測試

接下來咱們要試着讓這個窗口中展現出一個"Hello World!"字體

  1. storyboard文件中添加Label

咱們點擊 Main.storyboard 文件ui

storyboard_file

如今咱們來增長一個Label控件:

storyboard_choose_label

而後在隨後的輸入框中輸入 Label ,拖曳第一項至ViewController的View中。

storyboard_search

storyboard_label

  1. 給Label設置字體、約束

接下來爲這個Label增長一個水平居中和垂直居中的約束,並調大加粗字體。更換文字爲Hello World

注: 這裏的粒度因需幫助對於iOS開發不是很熟悉的人入門,所以較爲細了一點

storyboard_label_setting

修改以後的結果

storyboard_label_setting_result

運行下,就能夠看見大大的 Hello World 展現出來了

從Xib項目中初始化

Xib項目的初始化和storyboard的是相似的,只是不勾選 Use Storyboards

這裏的步驟,就不上圖片了

建立好後,文件結構以下:

helloworld_xib_file

這時候咱們點擊MainMenu.xib

xib_file

和storyboard同樣,咱們直接運行,將會出現一個空窗口。

接下來,咱們也是按照storyboard的步驟向xib中增長一個Hello World的label。增長的步驟也是相似這裏就很少講述了。

純代碼建立項目

ps: 純代碼建立 macOS app 費了我好長時間,最開始覺得只須要刪除 MainMenu.xib 就能夠了,別的步驟就和iOS中的同樣,初始化AppDelegate中的window就足夠了。可是,居然運行不起來,沒有效果。

  1. 項目的建立與運行

咱們仍是按照xib的步驟建立一個新項目。由於咱們是純代碼,所以刪除對應xib文件。

helloworld_code_file

接着咱們運行一下程序。將會發現以下錯誤:

2019-07-06 07:35:04.623078+0800 HelloWorld[20092:864470] Unable to load nib file: MainMenu, exiting

意思就是沒有找到對應的xib文件,因此程序退出了,畢竟咱們已經刪除了這個xib文件了。

因此接下來咱們點擊最上面的HelloWorld工程,並清空對應的入口文件(這裏和iOS的清空方式同樣)

helloworld_code_clean_entry

這裏的Main Interface表明着項目的可視化界面入口。由於是純代碼頁面,咱們並無這些入口,因此直接清空就能夠了。

再次嘗試運行,會發現沒有異常錯誤了,可是程序並無任何窗口彈出。

按照iOS的既有操做流程,咱們須要在AppDelegate的函數func applicationDidFinishLaunching(_ aNotification: Notification) 進行window的初始化與rootViewController的設置

不過在這以前,咱們先打印

func applicationDidFinishLaunching(_ aNotification: Notification) {
    print("Hello World!")
}
複製代碼

運行會發現,控制檯不輸出咱們的打印對象,說明方法並無執行,問題出如今哪裏了呢?

問題解釋

這裏就要說macOS App中比較坑的地方了(尤爲是和Swift結合),由於在xib和storyboard中建立應用,系統都會幫咱們默認指定一個入口。

隨後會執行main函數,這個main函數在iOS中的參數有4個

/// iOS main函數啓動方法
int UIApplicationMain(int argc, char * _Nullable argv[_Nonnull], NSString * _Nullable principalClassName, NSString * _Nullable delegateClassName);
複製代碼

其中後面兩個參數分別是UIApplication類的類名與AppDelegate類的類名。

在iOS中,咱們無需關心AppDelegate類的初始化,它會很天然的經過main函數裏的方法被初始化了。

這點在Swift中尤其明顯,咱們在Swift中通常點擊AppDelegate能夠看到@UIApplicationMain便至關於main函數了,所以在iOS中咱們若是採用一樣的操做,能夠發現iOS中的AppDelegate裏的func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool,將會被調用

而在macOS的app中,main函數以下:

APPKIT_EXTERN int NSApplicationMain(int argc, const char *__nonnull argv[__nonnull]);
複製代碼

比iOS中的對應參數少了後兩個,也就是說,若是咱們使用@NSApplicationMain的話,AppDelegate這個類將不會被自動初始化。這也許就是咱們添加的print函數不執行的緣由。

爲了進一步肯定真實緣由,咱們再作以下測試,重寫init()方法

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    @IBOutlet weak var window: NSWindow!

    override init() {
        super.init()
        print("執行到這了")
    }

    func applicationDidFinishLaunching(_ aNotification: Notification) {

    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }


}
複製代碼

執行代碼,發現控制檯什麼也沒有輸出。由此能夠肯定@NSApplicationMain並無幫咱們自動初始化AppDelegate類。所以咱們須要本身初始化了。

這裏引伸一下,爲何Xib和Storyboard都能成功初始化AppDelegate呢?

咱們看下xib工程中的MainMenu.xib文件

helloworld_appdelegate

由於xib中的Delegate的類爲AppDelegate,咱們繼續觀察

delegate_property

這裏咱們就能夠看出來了,在xib的啓動環境下,系統先讀取了MainMenu.xib,而後經過xib初始化了NSApplication而且初始化了AppDelegate


咱們已經知道了,@NSApplicationMain不會替咱們初始化AppDelegate,那麼咱們便須要本身對相關參數進行處理。

因此咱們在項目工程中新建main.swift文件

import Cocoa

/// 咱們須要先初始化AppDelegate,並強引用該對象防止其釋放
let delegate = AppDelegate()
/// 將delegate賦值
NSApplication.shared.delegate = delegate
/// 調用NSApplicationMain 傳入外界的入參
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
複製代碼

接下來將AppDelegate中的@NSApplicationMain刪除,並運行代碼

咱們能看見控制檯打印出了咱們的輸出了。下一步咱們就是展現出窗口了。

  1. 窗口的展現

咱們替換AppDelegate中原有的@IBOutlet weak var window: NSWindow!var window: NSWindow

並將init()函數更改以下:

override init() {
    /// 在swift中咱們須要在調用父類的初始化方法前,先初始化好子類的相關參數
    window = NSWindow(
        contentRect: CGRect(x: 0, y: 0, width: 800, height: 500), // 展現的區域
        styleMask: [.closable, .titled], // 展現的類型,這裏咱們暫時選擇 能夠關閉 與 展現標題欄
        backing: .buffered, // 全部其餘種類均被廢棄了,只剩.buffered
        defer: false // 是否推遲建立window
    )
    super.init()
}
複製代碼

更改func applicationDidFinishLaunching(_ aNotification: Notification)函數:

func applicationDidFinishLaunching(_ aNotification: Notification) {
    window.makeKeyAndOrderFront(nil)
}
複製代碼

運行程序,咱們能夠看見窗口彈出來了

  1. Hello world的展現

這裏就直接貼代碼了

func applicationDidFinishLaunching(_ aNotification: Notification) {
    /// 設置label須要的寬度
    let width: CGFloat = 300
    /// 設置label須要的高度
    let height: CGFloat = 30
    /// 獲取window的窗口的frame
    var frame = window.frame
    /// 設置x、y、width、height
    frame.origin.x = (frame.width - width) / 2
    frame.origin.y = (frame.height - height) / 2
    frame.size = CGSize(width: width, height: height)

    /// 由於MacOS中並沒有一個獨立的Label組件,因此咱們使用TextField替代
    let label = NSTextField(frame: frame)
    /// 設置字號
    label.font = .systemFont(ofSize: 30, weight: NSFont.Weight(rawValue: 3))
    /// 設置字符
    label.stringValue = "Hello World!"
    /// 設置居中
    label.alignment = .center
    /// 下面的都是取消NSTextField的一些與顯示無關屬性的
    /// 取消背景的邊框
    label.isBezeled = false
    /// 不可編輯
    label.isEditable = false
    /// 不可被選中
    label.isSelectable = false
    /// 不展現背景顏色
    label.drawsBackground = false
    /// 將label加入到window的contentView上
    window.contentView?.addSubview(label)
    /// 展現ContentView
    window.makeKeyAndOrderFront(nil)
}
複製代碼

運行下程序,咱們能夠看到一個帶着Hello World的窗口展現在屏幕上了

結尾

本文demo地址

macOS的app和iOS的app開發上有着許多類似的地方,可是又有不少差別很是大的地方。因此不少時候,咱們不能用開發iOS的經驗進行macOS的開發。

本文是《macOS軟件開發》系列的第一篇,有什麼不足與值得改進的地方,請在評論區指出或者直接在公衆號私信給我,看到必定回覆

gongzhonghao
相關文章
相關標籤/搜索