首先要說明iOS使用的Cocoa Touch框架,而macOS使用Cocoa框架!相比之下macOS的App的層次結構稍微復雜一些~
咱先創建好macOS的App后,默認產生的'Main.storyboard'含有了三個層次:Application Scene、Window Controller Scene、View Controller Scene。
Mac App標準結層次構:Application → (WindowController→Window) → (ViewController→View)
Application Scene層次
Application SceneWindow Controller Scene層次
Window Controller SceneView Controller Scene層次
View Controller Scene
而Window/WindowController和View/ViewController這四種實例是最常使用的!每處理一個窗口(Window)就會對這2個層次進行操作~
為了方便展示層次關系在代碼上的體現,把'Main.storyboard'中的Window Controller Scene層次和View Controller Scene層次都刪除掉(保留Application Scene層次-頂部的菜單欄還不錯),并把ViewController.Swift也刪除掉。
項目的入口還是'Main.storyboard'。
自己重新書寫Window Controller Scene層次和View Controller Scene層次!
先自己重新創建一個繼承自NSViewController名字為MainViewController的類:
通過'.xib'文件中控件的IBOutlet、IBAction,完成如下代碼的配置:
在"AppDelegate.Swift"文件中:
var mainWC: NSWindowController?//添加窗口控制器
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
//let mainVC = NSViewController()//??報錯:-[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: NSViewController in bundle (null).
//必須使用‘NSNib’形式的NSViewController類——含'xib'的
//let mainVC = MainViewController()
let mainVC = MainViewController(nibName: "MainViewController", bundle: Bundle.main)
let window = NSWindow(contentViewController: mainVC)
mainWC = NSWindowController(window: window)
//mainVC.myWC = mainWC //對應的窗口——設置與否可選
mainWC?.showWindow(nil)
}
注意:
a.必須使用‘NSNib’形式的NSViewController類實例(如上含'xib'的),否則運行報錯“-[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: NSViewController in bundle (null).
”!
b.通過NSWindow(contentViewController: mainVC)
創建window
(窗口)時,確定window
(窗口)和mainVC
(視圖控制器)的聯系——設置contentViewController
屬性;通過NSWindowController(window: window)
創建mainWC
(窗口控制器)時,確定mainWC
(窗口控制器)和window
(窗口)的聯系!
c.通過.showWindow(nil)
方法,展示mainWC
(窗口控制器)!
效果:展示該視圖控制器(MainViewController
類實例),點擊按鈕進行相應的響應~
這樣就可以使用自定義的視圖控制器了(窗口控制器也可以自定義)!
Window層的使用(NSWindow、NSWindowController)
大概講一下NSWindow和NSWindowController的基礎屬性~
演示代碼書寫如下:
@objc func clickOneButton() {
//(窗口風格)NSWindow.StyleMask - 首項:borderless
//(存儲類型)NSWindow.BackingStoreType - 首項:retained
let widnow = NSWindow(contentRect: NSMakeRect(100, 100, 300, 200), styleMask: NSWindow.StyleMask.titled, backing: NSWindow.BackingStoreType.buffered, defer: false)
print("create a window")
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2.0) {
print("delay after 2s","設置內容視圖contentView的背景色為cyan")
widnow.contentView?.wantsLayer = true
widnow.contentView?.layer?.backgroundColor = NSColor .cyan.cgColor
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2.0) {
print("delay after 4s","讓該窗口居于屏幕中央")
widnow .center()//讓該窗口居于屏幕中央
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2.0) {
print("delay after 6s","設置該窗口的標題")
widnow.title = "標題title"
//if #available(OSX 11.0, *) {
// widnow.subtitle = "subtitle 123456"
//}
}
}
}
let myWC = NSWindowController(); myWC.window = widnow
//let myWC = NSWindowController(window: widnow) //簡便寫法
myWC .showWindow(nil)//展示窗口
}
func addOneClickButton() {
let oneBtn = NSButton(frame: NSMakeRect(100, 100, 100, 100))
self.view .addSubview(oneBtn)
oneBtn.target = self; oneBtn.action = #selector(clickOneButton)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self .addOneClickButton()
}
效果:運行項目后,點擊‘Button’按鈕時——創建一個(相對于屏幕)frame為(100, 100, 300, 200)的窗口,延時2s后設置內容視圖(contentView
)的背景色為cyan,再延時2s后讓該窗口居于屏幕中央,再延時2s后設置該窗口的標題為"標題title"!每當再次點擊‘Button’按鈕,繼續重復執行上面的一系列操作(創建窗口,延時2s后操作、再延時2s后操作、再延時2s后操作)!
關于NSWindow和NSWindowController基礎屬性的更多詳細信息,請參考:
NSWindow:https://developer.apple.com/documentation/appkit/nswindow
NSWindowController:https://developer.apple.com/documentation/appkit/nswindowcontroller
WindowController層的特殊情況
-
1.創建NSWindow(窗口)所使用的視圖控制器(NSViewController),必須是使用‘NSNib’形式的NSViewController類實例(如上含'xib'的),否則運行報錯“
-[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: NSViewController in bundle (null).
”!直接使用工程原生的'Main.storyboard'對應的ViewController~ 通過該'Main.storyboard'文件中控件的IBOutlet、IBAction,完成如下代碼的配置:
完成代碼的配置-- 使用如下代碼創建并展示窗口控制器(NSWindowController)實例:窗口控制器
contentViewController
屬性是NSViewController
類型!@IBAction func leftBtnClick(_ sender: Any) { let sub1VC = NSViewController()//?//[General] -[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: NSViewController in bundle (null). let sub1Window = NSWindow(contentViewController: sub1VC) sub1WC = NSWindowController(window: sub1Window) sub1WC?.window?.title = "Left Window"http://設置標題 sub1WC? .showWindow(nil) }
運行時,點擊'Left'按鈕會報錯:
[General] -[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: NSViewController in bundle (null).
-- 使用如下代碼創建并展示窗口控制器(NSWindowController)實例:窗口控制器
contentViewController
屬性是 自定義但未使用‘NSNib’形式的WrongSub1ViewController
類型!未勾選'Also create XIB file for user interface'@IBAction func leftBtnClick(_ sender: Any) { let sub1VC = WrongSub1ViewController()//?//[General] -[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: MacDealWindow.WrongSub1ViewController in bundle (null). let sub1Window = NSWindow(contentViewController: sub1VC) sub1WC = NSWindowController(window: sub1Window) sub1WC?.window?.title = "Left Window"http://設置標題 sub1WC? .showWindow(nil) }
運行時,點擊'Left'按鈕會報錯:
[General] -[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: MacDealWindow.WrongSub1ViewController in bundle (null).
解決方法:
【一】必須使用‘NSNib’形式的NSViewController類型的實例作為窗口控制器的
contentViewController
——含'xib'的
使用如下代碼創建并展示窗口控制器(NSWindowController)實例:窗口控制器contentViewController
屬性是 自定義且使用‘NSNib’形式的Sub1ViewController
類型!勾選'Also create XIB file for user interface'@IBAction func leftBtnClick(_ sender: Any) { let sub1VC = Sub1ViewController(nibName: "Sub1ViewController", bundle: Bundle.main) //let sub1VC = Sub1ViewController()//?直接初始化 let sub1Window = NSWindow(contentViewController: sub1VC) sub1WC = NSWindowController(window: sub1Window) sub1WC?.window?.title = "Left Window"http://設置標題 sub1WC? .showWindow(nil) }
效果:運行時,點擊'Left'按鈕—1.創建一個標題為"Left Window"的窗口、2.不會報錯!
【二】直接使用‘NSNib’形式的NSWindowController類型的實例,作為窗口控制器!
使用如下代碼創建并展示窗口控制器(NSWindowController)實例:窗口控制器是 自定義且使用‘NSNib’形式的LeftWindowController
類型!勾選'Also create XIB file for user interface'該窗口控制器類型 — LeftWindowController:
'LeftWindowController.swift'文件'LeftWindowController.xib'文件@IBAction func leftBtnClick(_ sender: Any) { //【二】直接使用‘NSNib’形式的NSWindowController,作為窗口控制器 sub1WC = NSWindowController(windowNibName: "LeftWindowController") //sub1WC = NSWindowController() //無反應 //系統默認窗口控制器 //sub1WC = LeftWindowController2()//無反應 //未使用‘NSNib’形式的窗口控制器 sub1WC?.window?.title = "Left Window"http://設置標題 sub1WC? .showWindow(nil) }
效果:運行時,點擊'Left'按鈕—1.創建一個標題為"Left Window"的窗口、2.不會報錯!
對于“
//sub1WC = LeftWindowController2()//無反應 //未使用‘NSNib’形式的窗口控制器
”代碼,其所使用自定義的LeftWindowController2
類型的窗口控制器 沒有使用‘NSNib’形式!未勾選'Also create XIB file for user interface'更多屬性的效果,通過'
.window
'進行訪問和設置~
-
2.NSWindow代理的使用——
NSWindowDelegate
(繼續上面代碼邏輯)
實現代碼如下://MARK:NSWindowDelegate func windowWillMove(_ notification: Notification) { print("notification.object:\(String(describing: notification.object))") let nowWindow = notification.object; print("windowWillMove nowWindow:\(nowWindow as Any)") } func windowWillMiniaturize(_ notification: Notification) { print("windowWillMiniaturize") } func windowDidBecomeMain(_ notification: Notification) { print("windowDidBecomeMain") } func windowDidResignMain(_ notification: Notification) { print("windowDidResignMain") } var sub1WC: NSWindowController? @IBAction func leftBtnClick(_ sender: Any) { //【二】直接使用‘NSNib’形式的NSWindowController,作為窗口控制器 sub1WC = NSWindowController(windowNibName: "LeftWindowController") sub1WC?.window?.delegate = self//設置代理 sub1WC? .showWindow(nil) print("window:\(sub1WC?.window as Any)") }
效果:運行時,點擊'Left'按鈕—創建一個標題為"Left Window"的window窗口!
1.移動該window窗口時,會回調windowWillMove
方法、
2.分別選擇項目默認ViewController的窗口和該window窗口時進行切換,選中該window窗口時—回調windowDidBecomeMain
方法/取消選中該window窗口時—回調windowDidResignMain
方法、
3.點擊該窗口左側的最小化按鈕時—回調windowWillMiniaturize
方法和windowDidResignMain
方法,再在Dock欄選擇該window窗口實現最大化時—回調windowDidBecomeMain
方法!注意:通過代理方法返回的
(_ notification: Notification)
,使用notification
可獲取到當前 響應操作的窗口(該window窗口)——notification.object
let nowWindow = notification.object;//獲取當前 響應操作的窗口
更多請參考:NSWindowDelegate——https://developer.apple.com/documentation/appkit/nswindowdelegate
-
3.使用模態 — 指定窗口之外的其他窗口 不可操作!(繼續上面代碼邏輯)
核心方法:NSApplication .shared .runModal(for: 窗口對象)
-開始模態、NSApplication .shared .stopModal()
-結束模態~
在'LeftWindowController.swift'文件中:import Cocoa class LeftWindowController: NSWindowController { override func awakeFromNib() { super .awakeFromNib() print("LeftWindowController awakeFromNib") //self .addOneBtn()//避免重復添加 } override func windowDidLoad() { super.windowDidLoad() // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. print("LeftWindowController windowDidLoad") self .addOneBtn() } func addOneBtn() {//為該window,隨便添加一個按鈕 let btn = NSButton(frame: NSMakeRect(100, 100, 100, 100)) btn.target = self; btn.action = #selector(clickBtn) self.window?.contentView! .addSubview(btn) } @objc func clickBtn() { print("clickBtn") //打開了模態后,’DispatchQueue.main.asyncAfter‘延時操作不被執行~ //DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2.0) { // print("LeftWindowController內——延時2s操作,不會被調用") //} print("LeftWindowController內——結束模態 \(NSDate())") NSApplication .shared .stopModal()//結束模態 } }
在'ViewController.swift'中:
import Cocoa class ViewController: NSViewController ,NSWindowDelegate { //MARK:NSWindowDelegate func windowWillClose(_ notification: Notification) { print("windowWillClose") print("windowWillClose 結束模態 \(NSDate())") NSApplication .shared .stopModal()//結束模態 }//窗口關閉的響應——因為sub1WC的window窗口執行了’.close()‘方法(在LeftWindowController.swift文件中) override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } var sub1WC: LeftWindowController? @IBAction func leftBtnClick(_ sender: Any) { //【二】直接使用‘NSNib’形式的NSWindowController,作為窗口控制器 sub1WC = LeftWindowController(windowNibName: "LeftWindowController") //sub1WC = LeftWindowController()//?//沒有window窗口被創建 sub1WC?.window?.delegate = self; sub1WC? .showWindow(self) //sub1WC?.loadWindow()//加載window窗口 print(sub1WC as Any) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2.0) { [self] in print("開始模態") NSApplication .shared .runModal(for: (sub1WC?.window)!)//開始模態-無法到其他窗口操作 //模態結束后,才會繼續執行’DispatchQueue.main.asyncAfter‘延時 DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2.0) { print("結束模態 \(NSDate())") NSApplication .shared .stopModal()//結束模態 } } } @IBAction func rightBtnClick(_ sender: Any) { } override var representedObject: Any? { didSet { // Update the view, if already loaded. } } }
效果1:運行成功后默認的窗口A可以進行正常操作;點擊'Left'按鈕創建新窗口B后窗口A還可以正常操作,但延時2s后開始模態就只有新窗口B可以進行操作(默認的窗口A就不可進行操作了);點擊'Button'按鈕結束模態后默認的窗口A又可以進行操作了!
直接調用‘NSApplication .shared .stopModal()//結束模態’的方法注:在'LeftWindowController.swift'文件中將模態結束后,(默認的'ViewController.swift'中)才會繼續執行’
DispatchQueue.main.asyncAfter
‘延時!
效果2:運行成功后默認的窗口A可以進行正常操作;點擊'Left'按鈕創建新窗口B后窗口A還可以正常操作,但延時2s后開始模態就只有新窗口B可以進行操作(默認的窗口A就不可進行操作了);點擊新窗口B的左上方‘關閉’按鈕后在windowWillClose
回調方法中結束模態后默認的窗口A又可以進行操作了!代理方法`windowWillClose`中,調用‘NSApplication .shared .stopModal()//結束模態’的方法對應的OC代碼:
[[NSApplication sharedApplication] runModalForWindow:self.userLoginWinodwC.window];//開始模態-無法到其他窗口操作 [[NSApplication sharedApplication] stopModal];//結束模態 //NSModalSession sessionCode = [[NSApplication sharedApplication] beginModalSessionForWindow:self.userLoginWinodwC.window]; //[[NSApplication sharedApplication] endModalSession:sessionCode];
-
4.進行判空處理——避免重復創建窗口!(繼續上面代碼邏輯)
-- 4-1.未進行判空處理——在'ViewController.swift'中:import Cocoa class ViewController: NSViewController ,NSWindowDelegate { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } var sub1WC: LeftWindowController? func createAndShowWindow() { //【二】直接使用‘NSNib’形式的NSWindowController,作為窗口控制器 sub1WC = LeftWindowController(windowNibName: "LeftWindowController") sub1WC? .showWindow(nil) print(sub1WC as Any) } @IBAction func leftBtnClick(_ sender: Any) { self .createAndShowWindow() } @IBAction func rightBtnClick(_ sender: Any) { } override var representedObject: Any? { didSet { // Update the view, if already loaded. } } }
效果:點擊'Left'按鈕創建新窗口B,多次點擊則多次創建新窗口B(2/3/4/5……)——均是不同的對象!
注:會創建多個新窗口LeftWindowController對象
-- 4-2.進行了判空處理——在'ViewController.swift'中:import Cocoa class ViewController: NSViewController ,NSWindowDelegate { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } var sub1WC: LeftWindowController? func createAndShowWindow() { //【二】直接使用‘NSNib’形式的NSWindowController,作為窗口控制器 sub1WC = sub1WC != nil ? sub1WC : LeftWindowController(windowNibName: "LeftWindowController")//判空,避免重復創建 sub1WC? .showWindow(nil) print(sub1WC as Any) } @IBAction func leftBtnClick(_ sender: Any) { self .createAndShowWindow() } @IBAction func rightBtnClick(_ sender: Any) { } override var representedObject: Any? { didSet { // Update the view, if already loaded. } } }
效果:第一次點擊'Left'按鈕創建新窗口B,后續多次點擊只展示該新窗口B——都是相同的對象!
注:不會創建多個新窗口LeftWindowController對象
- 5.自定義一個提示窗口!
參考:《macOS警告/提示視圖 — NSAlert、自定義WindowController》
-
6.設置窗口的尺寸、位置——使用window的“
.setContentSize()
”方法和“.setFrameOrigin()
”方法~初始的默認尺寸:480x270(如下圖,在"Main.storyboard"中查看)
Main.storyboard在'ViewController.swift'文件中:
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { [self] in //設置window的尺寸、位置 self.view.window?.setContentSize(NSMakeSize(300, 300)) self.view.window?.setFrameOrigin(NSMakePoint(100, 100)) //self.view.window?.frameRect(forContentRect: NSMakeRect(100, 100, 300, 300))//?? DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { [self] in //設置window的尺寸、位置 self.view.window?.setContentSize(NSMakeSize(500, 500)) self.view.window?.setFrameOrigin(NSMakePoint(200, 200)) //self.view.window?.frameRect(forContentRect: NSMakeRect(200, 200, 500, 500))//?? } } }
效果:啟動App后,窗口的尺寸是默認的480x270!延時3s后尺寸變為300x300、起點坐標為(100, 100)!再延時3s后尺寸變為500x500、起點坐標為(200, 200)!
VIewController層的使用(NSVIewController、對應的view)
上面的情況是:在Window層中包含了自己對應的VIewController層,即一個窗口中一個NSVIewController的架構!
然后就可以在對應的self.view
上進行視圖布局~
還有一種情況是:一個(Window層)窗口中,可以有多個NSVIewController~
直接使用系統的NSViewController,初始化后的實例來進行操作:
let testVC = NSViewController()//?//Failed to set (contentViewController) user defined inspected property on (NSWindow): -[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: NSViewController in bundle (null). testVC.view.wantsLayer = true testVC.view.layer?.backgroundColor = NSColor.red.cgColor
直接使用系統的NSViewController,會報錯“
Failed to set (contentViewController) user defined inspected property on (NSWindow): -[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: NSViewController in bundle (null).
”,并且不能使用該視圖控制器!
自定義一個(繼承自NSViewController類)未使用‘NSNib’形式的視圖控制器:(GYHNoXibViewController)未勾選'Also create XIB file for user interface'再初始化后的實例來進行操作:
let testVC = GYHNoXibViewController()//?//Failed to set (contentViewController) user defined inspected property on (NSWindow): -[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: MacDealViewController.GYHNoXibViewController in bundle (null). testVC.view.wantsLayer = true testVC.view.layer?.backgroundColor = NSColor.red.cgColor
使用自定義但未使用‘NSNib’形式**的視圖控制器,會報錯“
Failed to set (contentViewController) user defined inspected property on (NSWindow): -[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: MacDealViewController.GYHNoXibViewController in bundle (null).
”,并且不能使用該視圖控制器!
這個跟上面Window層討論的情況相似,必須要使用‘NSNib’形式的NSViewController類實例(如下含'xib'的):
使用‘NSNib’形式的NSViewController類實例~
自定義一個(繼承自NSViewController類)使用‘NSNib’形式的視圖控制器:(GYHHasXibViewController)
勾選'Also create XIB file for user interface'再初始化后的實例來進行操作:
let testVC = GYHHasXibViewController() testVC.view.wantsLayer = true testVC.view.layer?.backgroundColor = NSColor.red.cgColo
結論:使用自定義并且使用‘NSNib’形式的視圖控制器,不會報錯且可以使用該視圖控制器!
操作代碼如下:使用 自定義且使用了‘NSNib’形式的視圖控制器—(GYHHasXibViewController)
import Cocoa
let Button_TAG = 1000
class ViewController: NSViewController {
var orderVC: NSViewController?
var settingVC: NSViewController?
var userVC: NSViewController?
var currentVC: NSViewController?//當前選中的VC
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//初始化視圖控制器
self.orderVC = GYHHasXibViewController()
self.orderVC?.view.wantsLayer = true
self.orderVC?.view.layer?.backgroundColor = NSColor.red.cgColor //紅
self.settingVC = GYHHasXibViewController()
self.settingVC?.view.wantsLayer = true
self.settingVC?.view.layer?.backgroundColor = NSColor.green.cgColor //綠
self.userVC = GYHHasXibViewController()
self.userVC?.view.wantsLayer = true
self.userVC?.view.layer?.backgroundColor = NSColor.blue.cgColor //藍
//添加子視圖控制器VC
self .addChild(self.orderVC!)
self .addChild(self.userVC!)
self .addChild(self.settingVC!)
self.currentVC = self.orderVC//當前選中的VC(初次)
self.view .addSubview(self.currentVC!.view)
let margin: CGFloat = 10.0
let btn_W: CGFloat = 100.0
let btn_H: CGFloat = 30.0
let arr = ["訂單", "設置", "個人信息"]
for i in 0..<arr.count {
let btn = NSButton(frame: NSMakeRect(margin, margin + (margin + btn_H)*CGFloat(i), btn_W, btn_H))
btn.title = arr[i];
btn.tag = i + Button_TAG
self.view .addSubview(btn)
btn.target = self; btn.action = #selector(clickButton)
}
}
@objc func clickButton(btn: NSButton) {//點擊各按鈕
switch (btn.tag - Button_TAG) {
case 0: do {//"訂單"
self .new_prensentToVC(toVc: self.orderVC as! GYHHasXibViewController)
}
case 1: do {//"設置"
self .new_prensentToVC(toVc: self.settingVC as! GYHHasXibViewController)
}
case 2: do {//"個人信息"
self .new_prensentToVC(toVc: self.userVC as! GYHHasXibViewController)
}
default:
break
}
}
func new_prensentToVC(toVc: GYHHasXibViewController) {
if (self.currentVC == toVc) {//當前選中的VC 就是 要跳轉的VC
return
}
print("self.currentVC:\(self.currentVC as Any), tovc:\(toVc)")
self .transition(from: self.currentVC!, to: toVc, options: NSViewController.TransitionOptions()) {
self.currentVC = toVc//當前選中的VC
}
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
效果:a.選擇相應"訂單"/"設置"/"個人信息"按鈕,就會轉到相應的界面(紅/綠/藍)!b.如果‘當前選中的VC’就是‘要跳轉的VC’,則不進行跳轉操作(‘open func transition(from fromViewController: NSViewController, to toViewController: NSViewController, options: NSViewController.TransitionOptions = [], completionHandler completion: (() -> Void)? = nil)
’方法)!
在"Debug View hierarchy"中查看視圖層次:
Tips:在
self.view
視圖里面,可以使用一個子視圖(作為容器視圖)來放入 上述自定義的視圖控制器!
(這點與iOS中代碼操作類似~)self.view .addSubview(self.currentVC!.view)
換為
self.view .addSubview(self.currentVC!.view)//可注釋、可不注釋 //單獨使用(容器)子視圖,來存放各VC對應的view let containerV = NSView(frame: NSMakeRect(150, 10, 300, 220)) self.view .addSubview(containerV) containerV .addSubview(self.currentVC!.view)
效果:a.對應自定義的視圖控制器放在了子視圖(作為容器視圖)里面了!b.選擇相應"訂單"/"設置"/"個人信息"按鈕跳,會轉到相應的界面(紅/綠/藍)!c.如果‘當前選中的VC’就是‘要跳轉的VC’,則不進行跳轉操作(‘open func transition(from fromViewController: NSViewController, to toViewController: NSViewController, options: NSViewController.TransitionOptions = [], completionHandler completion: (() -> Void)? = nil)
’方法)!在"Debug View hierarchy"中查看視圖層次:
goyohol's essay