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