zoukankan      html  css  js  c++  java
  • Swift之macOS开发中NSWindow, NSWindowController, NSView, NSViewController的关系

    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 

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

  • 相关阅读:
    phpStudy for Linux (lnmp+lamp一键安装包)
    eq,neq,gt,lt等表达式缩写
    lnmp环境的使用
    lnmp环境的搭建
    箭头函数中的this
    Vue中实现路由懒加载及组件懒加载
    Vue项目中实现路由按需加载(路由懒加载)的3中方式:
    判断数据类型的方式以及各自的优缺点
    最近工作中踩的坑
    7种方法实现CSS左侧固定,右侧自适应布局
  • 原文地址:https://www.cnblogs.com/sundaymac/p/10304644.html
Copyright © 2011-2022 走看看