macOS学习笔记--使用纯代码构建macOS应用
NO.1 The Start
起初,由于拖延症,这篇文章我不想发的,但是纯代码构建项目的demo又写好了.前两天群里的朋友又问我要纯代码的demo.索性就借着机会把demo扔出来好了.给自己的懒癌加强一针.
NO.2 The Message
首先,从项目的结构开始看起.(因为懒,并没有做MVC分层,只是完成了基本的实现)
首先,最大的问题就是main.swift
这个入口文件.默认的macOS工程是没有这个文件的.既然要用纯代码构建,那就把info.plist
文件里面的指定Storyboard文件的部分删掉.这个跟iOS开发还是一模一样的.然后删掉Main.storyboard
这个文件,反正没用了.删掉就是了.
然后,开始手写入口的main函数.在这里,你需要做两件事.第一,就是在实例化NSMenu对象.因为把storyboard删掉了,所以如果不实例化NSMenu的话,你得程序启动起来,左上角的菜单将空空如也.第二,就是手动实例化应用.因为是从头开始写,所以之前自动帮你生成的东西你也要自己写一遍(阿西巴,代码好多..当然了,快捷键也要自己绑定,这时候,你就可以随意开脑洞了,比如,⌘+Q改成新建……代码如下:
import Cocoa
func mainMenu() -> NSMenu {
let mainMenu = NSMenu()
let mainAppMenuItem = NSMenuItem(title: "Application", action: nil, keyEquivalent: "")
let mainFileMenuItem = NSMenuItem(title: "File", action: nil, keyEquivalent: "")
mainMenu.addItem(mainAppMenuItem)
mainMenu.addItem(mainFileMenuItem)
let appMenu = NSMenu()
mainAppMenuItem.submenu = appMenu
let appServicesMenu = NSMenu()
NSApp.servicesMenu = appServicesMenu
appMenu.addItem(withTitle: "About", action: nil, keyEquivalent: "")
appMenu.addItem(NSMenuItem.separator())
appMenu.addItem(withTitle: "Preferences...", action: nil, keyEquivalent: ",")
appMenu.addItem(NSMenuItem.separator())
appMenu.addItem(withTitle: "Hide", action: #selector(NSApplication.hide(_:)), keyEquivalent: "h")
appMenu.addItem({ ()->NSMenuItem in
let m = NSMenuItem(title: "Hide Others", action: #selector(NSApplication.hideOtherApplications(_:)), keyEquivalent: "h")
m.keyEquivalentModifierMask = NSEvent.ModifierFlags([.command, .option])
return m
}())
appMenu.addItem(withTitle: "Show All", action: #selector(NSApplication.unhideAllApplications(_:)), keyEquivalent: "")
appMenu.addItem(NSMenuItem.separator())
appMenu.addItem(withTitle: "Services", action: nil, keyEquivalent: "").submenu = appServicesMenu
appMenu.addItem(NSMenuItem.separator())
appMenu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
let fileMenu = NSMenu(title: "File")
mainFileMenuItem.submenu = fileMenu
fileMenu.addItem(withTitle: "New...", action: #selector(NSDocumentController.newDocument(_:)), keyEquivalent: "n")
return mainMenu
}
autoreleasepool {
let app = NSApplication.shared //创建应用
let delegate = AppDelegate()
app.delegate = delegate //配置应用代理
app.mainMenu = mainMenu() //配置菜单,mainMenu 函数需要前向定义,否则编译错误
app.run() //启动应用
}
iOS的程序都是单窗口的,所以没有WindowController
这个概念,而macOS不一样,如果是单窗口就坏事了.所以引入了WindowController
这个概念,其实这个跟View
和ViewController
是差不多的.所以对Window
的所有操作直接写在WindowController
里面就好了.对View
的操作写在ViewController
里面.
由于是纯代码工程.所以我们需要手动创建自己的WindowController
和ViewController
.然后,在AppDelegate.swift
里面对WindowController
进行实例化.注意注释掉@NSApplicationMain
.最后使用WindowController
的showWindow
方法把这个窗口显示出来.
class AppDelegate: NSObject, NSApplicationDelegate {
lazy var windowController: KZWindowController = {
let windowController = KZWindowController()
return windowController
}()
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
self.windowController.showWindow(self)
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
然后,在WindowController
里面初始化Window
.定义Window
的大小,座标以及各种属性.然后重写init
方法和init?(coder:)
方法.最后,在init
方法里面实例化ViewController
.
class KZWindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
}
lazy var myWindow: NSWindow? = {
let frame: CGRect = CGRect(x: 0, y: 0, width: 400, height: 280)
let style: NSWindow.StyleMask = [.titled,.closable,.resizable]
let back: NSWindow.BackingStoreType = .buffered
let window: KZWindow = KZWindow(contentRect: frame, styleMask: style, backing: back, defer: false)
window.title = "Test Window"
window.windowController = self
return window
}()
lazy var viewController: KZViewController = {
let viewController = KZViewController()
return viewController
}()
override init(window: NSWindow?) {
super.init(window: window)
self.window = self.myWindow
self.contentViewController = self.viewController
self.window?.center()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
剩下的在ViewController
里面实例化View
就跟iOS差不了太多了.
class KZViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
}
lazy var myView: KZView = {
let frame: CGRect = CGRect(x: 0, y: 0, width: 400, height: 300)
let view: KZView = KZView(frame: frame)
return view
}()
override init(nibName nibNameOrNil: NSNib.Name?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
self.view = self.myView
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
最后,⌘+R运行一下,只要没有语法错误,这时候应用就已经启动了.然后你就会发现,WTF!!!写了这么多,就一个白板…..就是下面这个.
啥都没有,干净的像是白瓷砖….所以说,没有什么特殊的需求,不要使用纯代码….第一是累,第二嘛,就是麻烦.很多东西明明自动处理好了,但是还是要自己手写.比如上面的NSMenu
.如果真的要使用代码的话,建议保留NSMenu
相关的视图可视化部分.能省很多的功夫,少写很多不必要的代码.
NO.3 The Code
https://github.com/KarlZeo/macOSNoSBXib
NO.4 Thanks for
剑指人心大佬的入门书籍以及Demo.
NO.5 ChangeLog
2018.03.26 代码升级Swift 4
,替换部分图片为代码块.
2017.06.30 发布,代码基于Swift 3.1
.