掘金好像沒有macOS開發專區,所以只好發到iOS專區裏了-_-git
最近由於突然間有一些靈感,想要作一款基於macOS的app。由於以前一直在開發iOS,感受macOS應該也是相似,結果一上手,差點被勸退。所以決定慢慢來,將本身的心得記錄下來。github
這系列文章不會從講述Swift或Objective-c語言的語法細節。所以若是是純小白開發,請先行自學語言。macos
注: 文章系列將會用Swift語言進行所有內容的開發swift
首先咱們須要選擇macOS,選擇 Cocoa App
,接下來點擊 Nextapp
從storyboard
建立工程應該是我最不喜歡的一種方式了,由於以爲故事版這種方式與代碼對應關係很弱,並且它又一樣擁有着xib修改起來費時費力的缺點。ide
在這裏咱們輸入工程名HelloWorldStoryBoard,而後選擇項目存儲地址後。咱們的工程目錄將如這個樣子函數
咱們直接按住 Command
+ R
運行程序,將會有一個窗口展現出來,裏面什麼都沒有測試
接下來咱們要試着讓這個窗口中展現出一個"Hello World!"字體
咱們點擊 Main.storyboard 文件ui
如今咱們來增長一個Label控件:
而後在隨後的輸入框中輸入 Label ,拖曳第一項至ViewController的View中。
接下來爲這個Label增長一個水平居中和垂直居中的約束,並調大加粗字體。更換文字爲Hello World
。
注: 這裏的粒度因需幫助對於iOS開發不是很熟悉的人入門,所以較爲細了一點
修改以後的結果
運行下,就能夠看見大大的 Hello World 展現出來了
Xib項目的初始化和storyboard的是相似的,只是不勾選 Use Storyboards
這裏的步驟,就不上圖片了
建立好後,文件結構以下:
這時候咱們點擊MainMenu.xib
和storyboard同樣,咱們直接運行,將會出現一個空窗口。
接下來,咱們也是按照storyboard的步驟向xib中增長一個Hello World的label。增長的步驟也是相似這裏就很少講述了。
ps: 純代碼建立 macOS app 費了我好長時間,最開始覺得只須要刪除 MainMenu.xib 就能夠了,別的步驟就和iOS中的同樣,初始化AppDelegate中的window就足夠了。可是,居然運行不起來,沒有效果。
咱們仍是按照xib的步驟建立一個新項目。由於咱們是純代碼,所以刪除對應xib文件。
接着咱們運行一下程序。將會發現以下錯誤:
2019-07-06 07:35:04.623078+0800 HelloWorld[20092:864470] Unable to load nib file: MainMenu, exiting
意思就是沒有找到對應的xib文件,因此程序退出了,畢竟咱們已經刪除了這個xib文件了。
因此接下來咱們點擊最上面的HelloWorld工程,並清空對應的入口文件(這裏和iOS的清空方式同樣)
這裏的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文件
由於xib中的Delegate的類爲AppDelegate
,咱們繼續觀察
這裏咱們就能夠看出來了,在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
刪除,並運行代碼
咱們能看見控制檯打印出了咱們的輸出了。下一步咱們就是展現出窗口了。
咱們替換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)
}
複製代碼
運行程序,咱們能夠看見窗口彈出來了
這裏就直接貼代碼了
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的窗口展現在屏幕上了
macOS的app和iOS的app開發上有着許多類似的地方,可是又有不少差別很是大的地方。因此不少時候,咱們不能用開發iOS的經驗進行macOS的開發。
本文是《macOS軟件開發》系列的第一篇,有什麼不足與值得改進的地方,請在評論區指出或者直接在公衆號私信給我,看到必定回覆