掘金好像没有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软件开发》系列的第一篇,有什么不足与值得改进的地方,请在评论区指出或者直接在公众号私信给我,看到必定回复