MacOS开发02-窗口对象

1,699 阅读6分钟

前言

几乎每一个程序都有窗口界面,了解窗口及相关对象的概念非常重要,开发窗口界面和窗口内的视图元素大概有三种方式。

  1. StoryBoard
  2. XIB
  3. 手写代码

本文主要介绍使用StoryBoard来创建和管理窗口。

创建项目

勾选StoryBoard和Swift,命名为:02.Window

image.png

打开: Main.storyboard 文件,该文件是StoryBoard设计文件,就在这里管理窗口和窗口内的视图

几个关键对象

看到Main.storyboard里的内容你可能有点迷惑,刚开始我也是的,先介绍几个关键对象和之间的关系 有助于你理解使用StoryBoard。

关于NS前缀

你会发现在Mac平台上很多类的前缀都是NS,那么NS代表什么意思呢?其实是历史原因,乔布斯被迫离开苹果公司后创立了NeXT公司,而回归苹果后发布的第一个操作系统叫做:NeXTSTEP,后面慢慢衍生到Mac系统中,将代码合并时,由于很多基础代码来自NeXTSTEP,所以这些类取其前缀NeXTSTEP,以标识来自NeXTSTEP操作系统。

NSWindow

顾名思义,NS是前缀,Window是窗口,就是你所看到的一个窗口的对象。他只是一个窗口容器,展示的内容需要有window里面的View来提供,也就是下面的NSView。

By the way, Swift是面向对象的编程语言,心中要牢记,万物皆对象。

NSWindow还负责接受用户的鼠标、键盘等系统事件。

窗口对象分为键盘窗口(Key Window)和主窗口(Main Window)

  • 键盘窗口就是可以接受用户的键盘、鼠标、触控板事件的窗口
  • 主窗口就是活动窗口,一般为获取焦点的窗口

同一时刻只能有一个键盘窗口和一个主窗口,键盘窗口和一个主窗口可以为同一个窗口

NSWindowController

窗口控制器,用来管理window的控制器。能够管理并从xibstoryboard文件中加载的窗口视图,以及管理不同场景多个界面窗口的切换

NSWindowController 与 NSWindowController的关系

一个NSWindow 可能有也可能没有 NSWindowController,如果没有NSWindowController 该窗口关闭后系统可能会自动释放该窗口。

NSWindowController一般都会有一个NSWindow对象(或从contentViewController中获取window对象。)

NSView

视图对象,大多UI组件都是它的子类。你所看到的几乎所有东西都是View,NSWindow里展示的内容就是这个对象,你可以控制这个对象的大小、位置、样式甚至动画,来实现你想要的效果。

NSView是有层级的,你可以这么理解,最开始有一个NSView对象,把他理解为是一幅画的最初形态:一张白纸,然后你可能在里面画一个圆圈,这个圆圈又是一个NSView,画一个鸡蛋,又是一个NSView对象,圆圈和鸡蛋都属于这张白纸的子View(SubView), 鸡蛋里面可能会有一些轮廓,或者印上一些字(有些蛋的确会印字的)这些轮廓或字又都是NSView,它们是鸡蛋的子View(SubViews)。甚至,你在画上的每一笔都是一个子View。各种子View的绘制,就能造就一幅优美壮丽的画了。

NSViewController

视图控制器和窗口控制器类似,它主要负责管理视图的生命周期,同时管理子视图控制器,实现了不同视图控制器之间的界面切换控制。

一般每个页面都会有一个NSViewController自定义类来负责改页面的事件和业务逻辑处理,从xib/Storyboard中加载设计好的页面。

NSViewController必须有一个view 或者有contentViewController。

NSWindow 与 NSView 的关系

  1. 每一个窗口都依赖视图而存在,窗口必须有一个根视图才能展示,即内容视图。
  2. 每个视图都必须存在一个窗口中,通过self.view.window获取view的window。

NSWindow对象加载之前,即windowDidLoad执行时会先加载window对应的内容视图从而触发视图控制器的viewDidLoad方法,在viewDidLoad方法中窗口还没初始化完,所以无法获取到窗口,因此要在viewDidAppear方法中才能获取到。

NSWindowController 与 NSViewController的关系

NSWindowController和NSWindow之间是互为引用的关系。

NSWindow的内容视图contentView为NSView。 当NSWindowController配置了contentViewController时,那这个contentViewController下的view最终就是该NSWindowController下的window的contentView。

而view所在的window就是该NSWindowController下的window。下面有个关系图:

image.png

关于刚刚新建的StoryBoard

image.png

为窗口对象、视图对象、窗口控制器、视图控制器添加自定义类型

如果你想控制窗口或视图,添加事件处理,就需要重写对应的类,在StoryBoard中将指定的对应的类型。

创建先创建几个类型:

  1. 创建名称为:MyNSView的Cocoa class,继承NSView
  2. 创建名称为:NSWindow的Cocoa class,继承NSWindow
  3. 创建名称为:MyWindowController class,继承NSWindowController ViewController不需要创建,默认有了。

将这几个类在StoryBoard中设置

image.png

image.png

image.png

视图控制器的生命周期

修改ViewController中的代码,添加日志打印:

import Cocoa

class ViewController: NSViewController {

    override func loadView() {
        super.loadView()
        print("loadView")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        print("viewDidLoad")
    }
    
    override func viewWillAppear() {
        super.viewWillAppear()
        print("viewWillAppear")
    }
    override func viewWillDisappear() {
        super.viewWillDisappear()
        print("viewWillDisappear")

        
    }
    override func viewDidAppear() {
        super.viewWillDisappear()
        print("viewDidAppear")
    }
    override func viewDidDisappear() {
        super.viewDidDisappear()
        print("viewDidDisappear")
    }

    override func updateViewConstraints() {
        super.updateViewConstraints()
        print("updateViewConstraints")
    }
    
    override func viewWillLayout() {
        super.viewWillLayout()
        print("viewWillLayout")
        
    }
    
    override func viewDidLayout() {
        super.viewDidLayout()
        print("viewDidLayout")
    }
    
}

启动程序,查看打印为:

loadView
viewDidLoad
viewWillAppear
updateViewConstraints
viewWillLayout
viewDidLayout
viewDidAppear

窗口跳转

添加一个新的窗口控制器,实现页面跳转。

先在默认窗口中添加一个button,打开StoryBoard,选中NSView,按住快捷键:Command+Shift+L,输入:button,选择:Push Button. 按住拖动到窗口中,双击修改button文字为:页面跳转

image.png

再按住,Command+Option+Control+回车,打开 代码和设计 关联页面,按住Ctrl+拖动button按钮到代码中,新增一个事件方法为:tapButtonAction

image.png

选择Action,Name为:tapButtonAction,点击connect。 这样就添加好了事件函数,按钮点击时就会触发该函数

再拖动一个outlet,ctrl+鼠标拖动到ViewController类的上边部分,选择:outlet

image.png

再回到StoryBoard中,按右上角的加号(Command+Shift+L),输入:windowController, 拖动到Storyboard中,选中新增的windowController,打开右侧面板,在StoryBoard ID一栏填入:MyNewWindow。

image.png

在刚刚创建的新的窗口中拖入一个Wrapping label,双击修改内容为:Hello, This's New!

然后回到刚刚的按钮点击事件函数中,添加如下代码:

    @IBAction func tapButtonAction(_ sender: Any) {
        button.title = "已跳转"
        let windowController = storyboard?.instantiateController(withIdentifier: "MyNewWindow") as? NSWindowController
        windowController?.showWindow(self)
    }

启动项目,之后点击按钮就可以跳转新页面了

image.png

最后

这就是今天的所有内容,介绍了StoryBoard中设计的主要对象,和几个对象之间的关系,如何将页面事件与代码做关联,如何跳转页面(最基础),下节介绍在Cocoa上面几种页面跳转方式。