macOS App層次結構、使用WindowController層/VIewController層


首先要說明iOS使用的Cocoa Touch框架,而macOS使用Cocoa框架!相比之下macOS的App的層次結構稍微復雜一些~

創建macOS的App

咱先創建好macOS的App后,默認產生的'Main.storyboard'含有了三個層次:Application SceneWindow Controller SceneView Controller Scene
Mac App標準結層次構Application → (WindowController→Window) → (ViewController→View)

Mac App標準結層次構:Application → (WindowController→Window) → (ViewController→View)

Application Scene層次

Application Scene

Window Controller Scene層次

Window Controller Scene

View Controller Scene層次

View Controller Scene


Window/WindowControllerView/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的類:

繼承自NSViewController名字為MainViewController的類

通過'.xib'文件中控件IBOutletIBAction,完成如下代碼的配置

通過'.xib'文件,完成代碼的配置

在"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)

大概講一下NSWindowNSWindowController基礎屬性~
演示代碼書寫如下:

@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后操作)!


關于NSWindowNSWindowController基礎屬性更多詳細信息,請參考
NSWindow:https://developer.apple.com/documentation/appkit/nswindow
NSWindowController:https://developer.apple.com/documentation/appkit/nswindowcontroller

窗口對象:http://www.macdev.io/ebook/window.html



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'文件中控件IBOutletIBAction,完成如下代碼的配置

    完成代碼的配置

    -- 使用如下代碼創建并展示窗口控制器(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對象





  • 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

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容