美文网首页Mac开发
macOS App层次结构、使用WindowController

macOS App层次结构、使用WindowController

作者: goyohol | 来源:发表于2021-08-18 21:46 被阅读0次


    首先要说明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"//设置标题
        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"//设置标题
        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"//设置标题
          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"//设置标题
          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

    相关文章

      网友评论

        本文标题:macOS App层次结构、使用WindowController

        本文链接:https://www.haomeiwen.com/subject/ipzjqqtx.html