Adding Main Menu

Hi


When adding new file to a macOS project theres a MainMenu option, whats the use of it ? I mean each project have its pre mad Main Menu

so whats the use of this item and how to use it if requiered ?


Kindest Regards

Answered by Claude31 in 395505022

whatever changed i do to secondary menu in user interface .xib file it wont reflect at run time


Can you tell exactly what change ?

Did you notice that the help menu disappeared ?

And about says "About NewApplication", as defined

I also added a menu item below About. And it shows when I run.

So I just don't understand what you mean.


also if I need the ne menu ti be hidden and relaod the old one when windows lost and get focus what function I shoud overrride ?


To be hidden. Do you mean replaced by the original one ?

That is what happens when window closes

    override func viewDidDisappear() {
        super.viewDidDisappear()
        let appDelegate = NSApplication.shared.delegate as! AppDelegate
        NSApp.mainMenu = appDelegate.currentMainMenu
    }


To do the same when select /+ deselect window, use windowDidBecomeKey, as follows:


AppDelegate :


import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    var currentMainMenu: NSMenu?

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Insert code here to initialize your application
        currentMainMenu = NSApp.mainMenu
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }

    func setSecondaryMenu() {
       
        Bundle.main.loadNibNamed("SecondaryMenu", owner: self, topLevelObjects: nil)
        let items = NSApplication.shared.mainMenu?.items
        let firstItem = items?[0]
        let subMenu = firstItem?.submenu
        subMenu?.title = "NewApplication " // Only this can change the menu title
        subMenu?.font = NSFont.boldSystemFont(ofSize: 0)
    }

}



Add the delegate func to S1W2WC



import Cocoa

class S1W2WC: NSWindowController, NSWindowDelegate {

    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.
    }

    func windowDidMove (_ notification: Notification) {
        let newOrigin = self.window!.frame.origin
        print("S1W2WC Window Did Move:", newOrigin)
    }

    func windowDidResize (_ notification: Notification) {
        let newSize = self.window!.frame.size
        print("S1W2WC Window Did Resize:", newSize)
    }

    func windowDidResignKey(_ notification: Notification) {          // NEW

        let appDelegate = NSApplication.shared.delegate as! AppDelegate
        NSApp.mainMenu = appDelegate.currentMainMenu
    }

    func windowDidBecomeKey(_ notification: Notification) {          // NEW

        let appDelegate = NSApplication.shared.delegate as! AppDelegate
        appDelegate.setSecondaryMenu()
    }

}


In ViewController, no more need to set delegate:


import Cocoa

class ViewController: NSViewController { // }, NSWindowDelegate {     // NSWindowDelegate NO MORE NEEDED

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        print("ViewController")
        self.view.window?.isOpaque = false
        //        self.view.window?.backgroundColor = .clear
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }

    override func viewWillAppear() {
        super.viewWillAppear()
 
    }

    override func viewDidLayout() {
        // self.view.window!.delegate = self     // NO MORE NEEDED
    }

    @IBAction func ShowWindow(_ sender: Any) {
 
    }

/* Ammar S. Mitoori .. Show Window2 in Main Storboard by code */
    @IBAction func ShowWindowByCode(_ sender: Any) {
 
        let storyboard = NSStoryboard(name: "Main", bundle: nil)
        if let windowController = storyboard.instantiateController(withIdentifier: "S1W2") as? S1W2WC // NSWindowController
        {
          windowController.showWindow(self)
        }
    }

/* Ammar S. Mitoori .. Show Window1 in Secondary Storboard by code */
    @IBAction func ShowWindow2S2ByCode(_ sender: Any) {
 
        let storyboard = NSStoryboard(name: "Secondary", bundle: nil)
        if let windowController = storyboard.instantiateController(withIdentifier: "S2W1") as? NSWindowController
        {
            windowController.showWindow(self)
        }
    }

/*     NO MORE NEEDED
    func windowDidMove (_notification: Notification){
        let newOrigin = self.view.window!.frame.origin
        print("ViewController Window Did Move:", newOrigin)
    }

    func windowDidResize (_notification: Notification){
        let newSize = self.view.window!.frame.size
        print("ViewController Window Did Resize:", newSize)
    }
  */
}


SecondVC can be slightly simplified:


import Cocoa

class SecondVC: NSViewController { // , NSWindowDelegate {

    var parentWindowController: NSWindowController? // Helps keep a reference to the controller: needed to avoid reference to be released and let notifications flow

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do view setup here.
    }
 
    override func viewDidAppear() {
        super.viewDidAppear()
        parentWindowController = self.view.window!.windowController // Needed, otherwise notification do not go through
     //  23.11.2019      self.view.window!.delegate = parentWindowController as! S1W2WC.   IN FACT, NOT NEEDED
     
        let appDelegate = NSApplication.shared.delegate as! AppDelegate
        appDelegate.setSecondaryMenu()
     
    }
 
//      func windowDidMove(_ notification: Notification) {          // NSWindowDelegate function NOT NEEDED
//        let newOrigin = self.view.window!.frame.origin
//        print("windowDidMove new origin:", newOrigin)
//
//      }
//
//      func windowDidResize(_ notification: Notification) {         // NSWindowDelegate function NOT NEEDED
//        let newSize = self.view.window!.frame.size
//        print("windowDidResize new size:", newSize)
//      }

    override func viewDidDisappear() {
        super.viewDidDisappear()
    // Maybe superfluous now that we have the func in WindowController
        let appDelegate = NSApplication.shared.delegate as! AppDelegate
        NSApp.mainMenu = appDelegate.currentMainMenu
    }
 
}


I tested, it works. Tell if that works now for you.

Can you explain more the case ?


When adding new file to a macOS project

You have an existing project. Or is it a new project ?

Is it with storyboard or not ?

What file type do you add ? Of course a MacOS, but is it Cocoa class (which ?) or else ?


theres a MainMenu option, whats the use of it ?

Where is this MainMenu option ? At which step op creation of file ?


I mean each project have its pre mad Main Menu

Yes there is a pre made MainMenu in Application scene

Its a new macOS project, I didnt add a Cocoa class but theres an item to add below in the UI section

so there you find a menu file that can be added, just wondering why its there and how to use it and

blend it in the application.

Imagine the use case.


Depending on where you stand in your app you need to change drastically the main menu content.

Instead of modifying each item, you create a new menu.

You would select this newMainMenu with

NSApplication.sharedApplication().mainMenu = newMainMenu


where newMainMenu is the NSMenu loaded from the xib you created.

Or you could copu the existing mainMenu and modify what needs to be modified to be able to switch rapidly between the 2.


heve some explanation here

https://stackoverflow.com/questions/28781974/how-to-add-nsmenu-programmatically

and here

h ttps://jameshfisher.com/2017/03/20/how-is-mainmenu-xib-loaded/


Note: Do you consider using this ? If so, for what purpose ?


PS: you've left a few threads open. Would you be so kind to close those which are solved ?

Tthanks allot for the replay, sure I will 🙂

Hi


Now to test this I made a macOS Storyboard project and added a second window, and SecondViewController.Swift and made it

the view controller of the second window, then added new menu called SecondMenu.xib, what I want is to make the second menu

the main menu when the second window shows, obviously putting the line of code you sent in the ViewDidLoad function wont work

any advice please ? the links didnt help allot.


Kindest Regards

Here is the set up.


In AppDelegate, keep track of original mainMenu


import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    var currentMainMenu: NSMenu?

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Insert code here to initialize your application
        currentMainMenu = NSApp.mainMenu
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }

}


In The ViewController:


class SecondVC: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do view setup here.
    }

    override func viewDidAppear() {
        super.viewDidAppear()
        Bundle.main.loadNibNamed("SecondaryMenu", owner: self, topLevelObjects: nil)     // That's it
        // However, app name does not change in the menu yet
        // With the following, it will be updated !
        let items = NSApplication.shared.mainMenu?.items
        let firstItem = items?[0]
        let subMenu = firstItem?.submenu
        subMenu?.title = "NewApp"

    }

    override func viewDidDisappear() {
        super.viewDidDisappear()
        let appDelegate = NSApplication.shared.delegate as! AppDelegate
        NSApp.mainMenu = appDelegate.currentMainMenu     // Restore original menu
    }

}


If you set "NewApplication" for the title, it will not change.

But if you set "NewApp" or even "NewApplication ", it will change.

Thanks that was really helpful, but what I noticed is whatever changed i do to secondary menu in user interface

.xib file it wont reflect at run time ! also if I need the ne menu ti be hidden and relaod the old one when windows

lost and get focus what function I shoukd overrride ?

--

Kindest Regards

Accepted Answer

whatever changed i do to secondary menu in user interface .xib file it wont reflect at run time


Can you tell exactly what change ?

Did you notice that the help menu disappeared ?

And about says "About NewApplication", as defined

I also added a menu item below About. And it shows when I run.

So I just don't understand what you mean.


also if I need the ne menu ti be hidden and relaod the old one when windows lost and get focus what function I shoud overrride ?


To be hidden. Do you mean replaced by the original one ?

That is what happens when window closes

    override func viewDidDisappear() {
        super.viewDidDisappear()
        let appDelegate = NSApplication.shared.delegate as! AppDelegate
        NSApp.mainMenu = appDelegate.currentMainMenu
    }


To do the same when select /+ deselect window, use windowDidBecomeKey, as follows:


AppDelegate :


import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    var currentMainMenu: NSMenu?

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Insert code here to initialize your application
        currentMainMenu = NSApp.mainMenu
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }

    func setSecondaryMenu() {
       
        Bundle.main.loadNibNamed("SecondaryMenu", owner: self, topLevelObjects: nil)
        let items = NSApplication.shared.mainMenu?.items
        let firstItem = items?[0]
        let subMenu = firstItem?.submenu
        subMenu?.title = "NewApplication " // Only this can change the menu title
        subMenu?.font = NSFont.boldSystemFont(ofSize: 0)
    }

}



Add the delegate func to S1W2WC



import Cocoa

class S1W2WC: NSWindowController, NSWindowDelegate {

    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.
    }

    func windowDidMove (_ notification: Notification) {
        let newOrigin = self.window!.frame.origin
        print("S1W2WC Window Did Move:", newOrigin)
    }

    func windowDidResize (_ notification: Notification) {
        let newSize = self.window!.frame.size
        print("S1W2WC Window Did Resize:", newSize)
    }

    func windowDidResignKey(_ notification: Notification) {          // NEW

        let appDelegate = NSApplication.shared.delegate as! AppDelegate
        NSApp.mainMenu = appDelegate.currentMainMenu
    }

    func windowDidBecomeKey(_ notification: Notification) {          // NEW

        let appDelegate = NSApplication.shared.delegate as! AppDelegate
        appDelegate.setSecondaryMenu()
    }

}


In ViewController, no more need to set delegate:


import Cocoa

class ViewController: NSViewController { // }, NSWindowDelegate {     // NSWindowDelegate NO MORE NEEDED

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        print("ViewController")
        self.view.window?.isOpaque = false
        //        self.view.window?.backgroundColor = .clear
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }

    override func viewWillAppear() {
        super.viewWillAppear()
 
    }

    override func viewDidLayout() {
        // self.view.window!.delegate = self     // NO MORE NEEDED
    }

    @IBAction func ShowWindow(_ sender: Any) {
 
    }

/* Ammar S. Mitoori .. Show Window2 in Main Storboard by code */
    @IBAction func ShowWindowByCode(_ sender: Any) {
 
        let storyboard = NSStoryboard(name: "Main", bundle: nil)
        if let windowController = storyboard.instantiateController(withIdentifier: "S1W2") as? S1W2WC // NSWindowController
        {
          windowController.showWindow(self)
        }
    }

/* Ammar S. Mitoori .. Show Window1 in Secondary Storboard by code */
    @IBAction func ShowWindow2S2ByCode(_ sender: Any) {
 
        let storyboard = NSStoryboard(name: "Secondary", bundle: nil)
        if let windowController = storyboard.instantiateController(withIdentifier: "S2W1") as? NSWindowController
        {
            windowController.showWindow(self)
        }
    }

/*     NO MORE NEEDED
    func windowDidMove (_notification: Notification){
        let newOrigin = self.view.window!.frame.origin
        print("ViewController Window Did Move:", newOrigin)
    }

    func windowDidResize (_notification: Notification){
        let newSize = self.view.window!.frame.size
        print("ViewController Window Did Resize:", newSize)
    }
  */
}


SecondVC can be slightly simplified:


import Cocoa

class SecondVC: NSViewController { // , NSWindowDelegate {

    var parentWindowController: NSWindowController? // Helps keep a reference to the controller: needed to avoid reference to be released and let notifications flow

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do view setup here.
    }
 
    override func viewDidAppear() {
        super.viewDidAppear()
        parentWindowController = self.view.window!.windowController // Needed, otherwise notification do not go through
     //  23.11.2019      self.view.window!.delegate = parentWindowController as! S1W2WC.   IN FACT, NOT NEEDED
     
        let appDelegate = NSApplication.shared.delegate as! AppDelegate
        appDelegate.setSecondaryMenu()
     
    }
 
//      func windowDidMove(_ notification: Notification) {          // NSWindowDelegate function NOT NEEDED
//        let newOrigin = self.view.window!.frame.origin
//        print("windowDidMove new origin:", newOrigin)
//
//      }
//
//      func windowDidResize(_ notification: Notification) {         // NSWindowDelegate function NOT NEEDED
//        let newSize = self.view.window!.frame.size
//        print("windowDidResize new size:", newSize)
//      }

    override func viewDidDisappear() {
        super.viewDidDisappear()
    // Maybe superfluous now that we have the func in WindowController
        let appDelegate = NSApplication.shared.delegate as! AppDelegate
        NSApp.mainMenu = appDelegate.currentMainMenu
    }
 
}


I tested, it works. Tell if that works now for you.

Im doing the changes to a new App so it would be simpler in code, what I mean by changing the second menu for example

I delete the Help menu at design time, I delete the print menu, I add a new menu .. all this changes at run time goes away

its in this link if you like to have a look

h ttps://www.dropbox.com/s/w9khgm2ieqe2roa/Main%20Menu%20Demo.zip?dl=0

Your xib is secondMenu.xib


and you call SecondaryMenu


       Bundle.main.loadNibNamed("SecondaryMenu", owner: self, topLevelObjects: nil)

Cannot work.


If you keep the same xib name, change to


        Bundle.main.loadNibNamed("SecondMenu", owner: self, topLevelObjects: nil)     // That's it

Thanks allot the Window Controller direction is more neat )

Adding Main Menu
 
 
Q