Open a View from the menu

(Swift, macOS, storyboards)

How to open a view (not a window) by clicking a menu item?

Let's suppose I have one window and two views:

I try to open with Menu item: "open view 1" and item: "open view 2":

(Here I asked the same question. Claude 31 solved very well with a button in one of the views. Here I am asking the same but with a button in the menu or menu item: https://developer.apple.com/forums/thread/690644 )

(Be aware that I am a beginner and I try to learn. Please explain in a simple way if possible)

Answered by OOPer in 689112022

You should better do it in the usual way of macOS programming.

(You have no need to define your own nib, you have no need to define global variables.)

Assuming you already added menu items Open view 1 and Open view 2 as in the screen shot, and the Custom Classes of the two view controllers are FirstViewController and SecondViewController.

(The first may be ViewController, then you may need to replace FirstViewController to ViewController in the following code.)

1. Create a new file MyWindowController.swift and define the IBActions for Open view 1 and Open view 2 in it.

MyWindowController.swift:

import Cocoa

class MyWindowController: NSWindowController {
    
    @IBAction func openView1(_ sender :Any) {
        guard let storyboard = self.storyboard else {
            return
        }
        let firstViewController = storyboard.instantiateController(withIdentifier: "FirstView") as? FirstViewController
        self.window?.contentViewController = firstViewController
    }
    
    @IBAction func openView2(_ sender :Any) {
        guard let storyboard = self.storyboard else {
            return
        }
        let secondViewController = storyboard.instantiateController(withIdentifier: "SecondView") as? SecondViewController
        self.window?.contentViewController = secondViewController
    }
    
}

2. In the Interface Builder, change the Custom Class of your Window Controller to MyWindowController.

3. Connect the action of the menu item Open view 1 to openView1: of First Responder.

(A pop up list is shown when you release the mouse button, you can choose openView1: from the list.)

4. Connect the action of the menu item Open view 2 to openView2: of First Responder as well.


5. Run your code.

If you find something wrong, please tell me. I did test.

What I do is declare a global var (in a singleton in fact) to hold the windowController. myWindowController is of a subClass of NSWindowController, with its own nib. In the subClass MyWindowController, I have several init:

class MyWindowController: NSWindowController, NSWindowDelegate {

    override init(window: NSWindow!) {  
        // initialize what you need 
        super.init(window: window)
    }
    
    required init?(coder: (NSCoder?)) {
        // initialize what you need 
        super.init(coder: coder!)  
    }
    
    convenience init(someTag: Int) {  // Need a parameter to differentiate from basic init()
        self.init(windowNibName: NSNib.Name(rawValue: "MyNibName")) // NibName of the window
        // initialize what you need 
    }

}

The global var is initialized (in singleton) with:

myWindowController = MyWindowController(someTag: 0)  // in fact GlobalData.sharedGlobal.myWindowController =

Then the IBAction of menuItem to open the window is simply:

    @IBAction fileprivate func showMyWindow(_ sender: NSMenuItem) {
        myWindowController!.showWindow(self)   // in fact, GlobalData.sharedGlobal.myWindowController!.showWindow(self)
}

Then for the menuItems to change the view in the window:

    @IBAction fileprivate func loadView1(_ sender: NSMenuItem) {
        let storyboard: NSStoryboard = NSStoryboard(name: "Main", bundle: nil)
        let myViewController = storyboard.instantiateController(withIdentifier: "FirstView") as? FirstViewController
        self.view.window?.contentViewController = myViewController
  }

    @IBAction fileprivate func loadView2(_ sender: NSMenuItem) {
        let storyboard: NSStoryboard = NSStoryboard(name: "Main", bundle: nil)
        let myViewController = storyboard.instantiateController(withIdentifier: "SecondView") as? SecondViewController
        self.view.window?.contentViewController = myViewController
  }

Note: I did not test yet, but that should work.

Accepted Answer

You should better do it in the usual way of macOS programming.

(You have no need to define your own nib, you have no need to define global variables.)

Assuming you already added menu items Open view 1 and Open view 2 as in the screen shot, and the Custom Classes of the two view controllers are FirstViewController and SecondViewController.

(The first may be ViewController, then you may need to replace FirstViewController to ViewController in the following code.)

1. Create a new file MyWindowController.swift and define the IBActions for Open view 1 and Open view 2 in it.

MyWindowController.swift:

import Cocoa

class MyWindowController: NSWindowController {
    
    @IBAction func openView1(_ sender :Any) {
        guard let storyboard = self.storyboard else {
            return
        }
        let firstViewController = storyboard.instantiateController(withIdentifier: "FirstView") as? FirstViewController
        self.window?.contentViewController = firstViewController
    }
    
    @IBAction func openView2(_ sender :Any) {
        guard let storyboard = self.storyboard else {
            return
        }
        let secondViewController = storyboard.instantiateController(withIdentifier: "SecondView") as? SecondViewController
        self.window?.contentViewController = secondViewController
    }
    
}

2. In the Interface Builder, change the Custom Class of your Window Controller to MyWindowController.

3. Connect the action of the menu item Open view 1 to openView1: of First Responder.

(A pop up list is shown when you release the mouse button, you can choose openView1: from the list.)

4. Connect the action of the menu item Open view 2 to openView2: of First Responder as well.


5. Run your code.

If you find something wrong, please tell me. I did test.

Open a View from the menu
 
 
Q