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
相关文章
相关标签/搜索