• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Swift之macOS开发中NSWindow,NSWindowController,NSView,NSViewController的关系 ...

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

https://blog.csdn.net/fl2011sx/article/details/73252859

 

macOS使用的Cocoa框架,的确没有iOS使用的Cocoa Touch那么智能好用。有些地方逻辑很奇怪,还有一些看似很正常的功能它却没有提供,还需要自定义。这里就有一个很头疼的问题,关于这四个类的问题,他们之间到底是什么关系,如果摆脱了storyboard如何用代码实现?今天就来简单介绍一下。

    Xcode所提供的默认模板包括一个WindowController,还有一个ViewController,在ViewController中还有一个View,我们的控件一般都写在这个View中。而起始,storyboard把一个逻辑给简化了,关于Window,WindowController,View和ViewController,这四个类可以说是相互依存的。

 

    如果我们不使用storyboard,那么程序就会去读取AppDelegate中的代码(如果是用默认模板的话,把storyboard删除之后要记得在设置中把默认storyboard删除)。我们应用程序显示的第一个窗口就需要在此定义。由于Cocoa框架严格遵守着MVC模式,因此,要想在屏幕上显示一个窗口,那么一定就要拥有模型,视图和对应的控制器。那么,既然是要生成一个窗口,我们就需要一个NSWindow或其子类的实例。NSWindow有这样一个初始化函数:

 

public convenience init(contentViewController: NSViewController)

 

这里的意思是说,我们要一个窗口,那么窗口里究竟显示什么东西,是需要一个ViewController说了算的,所以我们还需要一个ViewController,而ViewController有这样一个构造函数:

public init?(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)

 

    既然有了视图控制器,那一定是用来显示视图的,那视图在哪里呢?一般是用xib文件(编译之后就成为nib文件)来编辑的,所以调用这个方法就可以加载nib文件。当然,如果你的View是用代码定义的,那在这里给两个参数传空就可以了,然后操作NSViewController的一个属性来改变它的视图:

 

open var view: NSView

 

    之后,有了window,我们还得需要一个控制器来把这个窗口显示在屏幕上(目前为止所有的数据还都是内存数据而已,我们还需要调用显示方法),所以就用到了NSWIndowController,它提供了一个构造函数:

 

public init(window: NSWindow?)

 

    这样就齐全了,我们可以看到,NSWindowController里会包含一个它要控制的NSWindow,而NSWindow需要一个NSViewController来管理具体显示的视图,最后还需要一个具体的NSView。当我们准备齐全这些以后,就可以调用NSWindowController的一个方法,显示窗口:

 

@IBAction open func showWindow(_ sender: Any?)

 

    关于这里的sender,官方的解释是动作的发起者,一般是应用程序代理,但是本人尝试,其实填什么都好像不影响结果,哪怕是nil,也可以正常显示。具体的含义还有待继续摸索。

 

    还有就是关于storyboard的建议,其实在做macOS开发的时候,storyboard并不好用,不像在iOS开发时那样得心应手,所以还是建议把视图的设计用xib,然后关于控制器的部分用代码来书写。但是也并不建议直接把storyboard删掉,因为用它来编辑状态栏的下拉菜单还是非常方便地,所以本人的做法是在storyboard中只留一个file menu,把其他的视图和控制器都删除掉。当然这样的话,在项目设置处的入口storyboard就必须还得是Main才行。

 

    接下来用一个具体的例子来说明上面的这一系列问题。我们制作一个简单的应用程序,它的主界面有两个按钮,当点击第一个按钮的时候创建一个新的窗口,当点击第二个按钮的时候也创建一个新的窗口,同时还关闭主窗口。

 

    分析上面的要求,我们肯定是需要3套内容,每一套里都应该含有一个WIndowController,一个Window,一个ViewController和一个View。

 

    首先,创建一个Cocoa工程

 

 

 

 

 

    之后,我们创建三个ViewController以及xib文件,Command+N,选择Cocoa Class,输入mainViewController,勾选xib文件:

 

    然后用同样的方法生成sub1ViewController和sub2ViewController: 

    对于sub1和sub2,我们只是能够区分就好,所以在xib里随便拖个控件什么的,能认清楚就行,而对于mainViewController.xib,我们需要两个按钮,并且还要关联到mainViewController.swift中点击方法,这里不再赘述,完成之后的效果如下:

 

 

 

    我们来重点编辑这两个函数,这里有一点需要注意的是,WindowController必须持久存在,否则会造成窗口闪退的现象,所以,我们要确保WindowController时刻存在一个引用,这样它才不会被ARC回收掉,那么最好的办法就是让他成为一个成员变量,这样就可以保持引用。而至于其他的对象,在WindowController内部会保持连接,所以只要WindowController在,它们就一定在,所以我们用局部变量来作为一个“接力手”就可以了。

 

    比如说我们要在btn1Click(_:)方法中显示视图1,那么首先要有一个ViewController来加载对应的xib文件,然后要创建一个窗口关联它,再把它给到WindowController中就可以了,具体代码如下:

 

//

 

//  mainViewController.swift

 

 

import Cocoa

 

 

class mainViewController: NSViewController {

 

 

    override func viewDidLoad() {

 

        super.viewDidLoad()

 

        // Do view setup here.

 

    }

 

    open var windowController: NSWindowController?

 

    var sub1WindowController: NSWindowController?

 

    @IBAction func btn1Click(_ sender: NSButton) {

 

        // 创建视图控制器,加载xib文件

 

        let sub1ViewController = NSViewController(nibName: "sub1ViewController", bundle: Bundle.main)

 

        // 创建窗口,关联控制器

 

        let sub1Window = sub1ViewController != nil ? NSWindow(contentViewController: sub1ViewController!) : nil

 

        // 初始化窗口控制器

 

        sub1WindowController = NSWindowController(window: sub1Window)

 

        // 显示窗口

 

        sub1WindowController?.showWindow(nil)

 

    }

 

    var sub2WindowController: NSWindowController?

 

    @IBAction func btn2Click(_ sender: NSButton) {

 

        // 同上

 

        let sub2ViewController = NSViewController(nibName: "sub2ViewController", bundle: Bundle.main)

 

        let sub2Window = sub2ViewController != nil ? NSWindow(contentViewController: sub2ViewController!) : nil

 

        sub2WindowController = NSWindowController(window: sub2Window)

 

        sub2WindowController?.showWindow(nil)

 

        // 加上一句关闭当前窗口

 

        windowController?.close()

 

    }

 

    

 

}

 

    需要说明的一点是,由于关闭窗口是WindowController管的,所以要想在ViewController里操作的话,就需要传入进来才行,所以这里的成员windowController就是如此。

 

    虽然我们这个逻辑实现了,但是现在打开应用程序还是没有窗口的,因为我们主窗口还没有显示出来,所以我们还需要在应用程序加载完毕后加载主窗口,所以还要在AppDelegate中对mainView实现一个相同的功能:

 

//

 

//  AppDelegate.swift

 

 

import Cocoa

 

 

@NSApplicationMain

 

class AppDelegate: NSObject, NSApplicationDelegate {

 

 

    var mainWindowController: NSWindowController?

 

 

    func applicationDidFinishLaunching(_ aNotification: Notification) {

 

        // Insert code here to initialize your application

 

        let mainViewController_ = mainViewController(nibName: "mainViewController", bundle: Bundle.main)

 

        let mainWindow = mainViewController_ != nil ? NSWindow(contentViewController: mainViewController_!) : nil

 

        mainWindowController = NSWindowController(window: mainWindow)

 

        mainViewController_?.windowController = mainWindowController

 

        mainWindowController?.showWindow(nil)

 

    }

 

 

    func applicationWillTerminate(_ aNotification: Notification) {

 

        // Insert code here to tear down your application

 

    }

 

 

 

}

--------------------- 

作者:fl2011sx 

来源:CSDN 

原文:https://blog.csdn.net/fl2011sx/article/details/73252859 

版权声明:本文为博主原创文章,转载请附上博文链接!


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Swift-----存储型属性、计算型属性、类型型属性发布时间:2022-07-13
下一篇:
【Swift】Timer定时器到底准不准确?发布时间:2022-07-13
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap