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
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!