https://blog.csdn.net/fl2011sx/article/details/73252859swift
macOS使用的Cocoa框架,的确没有iOS使用的Cocoa Touch那么智能好用。有些地方逻辑很奇怪,还有一些看似很正常的功能它却没有提供,还须要自定义。这里就有一个很头疼的问题,关于这四个类的问题,他们之间究竟是什么关系,若是摆脱了storyboard如何用代码实现?今天就来简单介绍一下。app
Xcode所提供的默认模板包括一个WindowController,还有一个ViewController,在ViewController中还有一个View,咱们的控件通常都写在这个View中。而起始,storyboard把一个逻辑给简化了,关于Window,WindowController,View和ViewController,这四个类能够说是相互依存的。框架
若是咱们不使用storyboard,那么程序就会去读取AppDelegate中的代码(若是是用默认模板的话,把storyboard删除以后要记得在设置中把默认storyboard删除)。咱们应用程序显示的第一个窗口就须要在此定义。因为Cocoa框架严格遵照着MVC模式,所以,要想在屏幕上显示一个窗口,那么必定就要拥有模型,视图和对应的控制器。那么,既然是要生成一个窗口,咱们就须要一个NSWindow或其子类的实例。NSWindow有这样一个初始化函数:ide
public convenience init(contentViewController: NSViewController)函数
这里的意思是说,咱们要一个窗口,那么窗口里究竟显示什么东西,是须要一个ViewController说了算的,因此咱们还须要一个ViewController,而ViewController有这样一个构造函数:.net
public init?(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)设计
既然有了视图控制器,那必定是用来显示视图的,那视图在哪里呢?通常是用xib文件(编译以后就成为nib文件)来编辑的,因此调用这个方法就能够加载nib文件。固然,若是你的View是用代码定义的,那在这里给两个参数传空就能够了,而后操做NSViewController的一个属性来改变它的视图:代理
open var view: NSViewcode
以后,有了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