9

I didn't use storyboard and xib, just used only code. I would like to add "Edit" Menu Programmatically. My Questions are

1) How to show "Edit" Menu / What codes need to input at comment Question 1)?

2) There are any actions provided from swift like copy & paste?

class TestManager: NSObject {

// ....
    override init() {
        let editMenuItems = [
            NSMenuItem(title: "Cut", action: nil(/* Question 2) */), keyEquivalent: ""),
            NSMenuItem(title: "Copy", action: nil, keyEquivalent: ""),
            NSMenuItem(title: "Paste", action: nil, keyEquivalent: ""),
        ]

        for editMenuItem in editMenuItems {
            self.editMenu.addItem(editMenuItem)
        }

        // Qustion 1) .. show "Edit" Menu
    }
}
Astin
  • 137
  • 1
  • 1
  • 9
  • Would you please state why you want to make an app withtout mainmenu if you actually need it? – qwerty_so Feb 28 '15 at 15:15
  • 4
    The reason why I use custom code is 1) What I want to make is similar to https://github.com/devxoul/allkdic (Custom Code) 2) If you know how to code a user interface, then you know what happens under the hood, whereas the same is not necessarily true of NIBs and Storyboards. – Astin Mar 08 '15 at 15:54
  • That, honestly, does not make any sense at all. If you need a menu (and you state that you do need it) then use the MainMenu as being placed in the Info.plist. – qwerty_so Mar 08 '15 at 19:42
  • 9
    I don't quite understand all the negatives. It's a perfectly reasonable question and I also want to know the answer also. I'm wanting to insert and append items. i.e. not static but dynamic. This is for a launcher application where apps are downloaded and launched from a dynamic menu. – hookenz Nov 30 '15 at 20:09

3 Answers3

6

You don't show where self.editMenu comes from.

In any case, you need to obtain the mainMenu from the NSApplication instance and add a menu item to that which has your menu as its submenu. So, something like:

var editMenuItem = NSMenuItem()
editMenuItem.title = "Edit"
var mainMenu = NSMenu()
mainMenu.addItem(editMenuItem)
mainMenu.setSubmenu(self.editMenu, forItem:editMenuItem)
NSApplication.sharedApplication().mainMenu = mainMenu

I don't work in Swift, so there are probably some mistakes in there.

As to what action selector to use for Edit menu items, the easiest thing for you to do is to create a main menu NIB just to examine it. Look at the action selectors used for the menu items of the ready-made Edit menu. You'll find that the Copy item uses the copy: selector, for example. That can be represented in Swift as just a string, "copy:".

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Thanks for the answer. I don't have enough reputation. I will try to do this way and comment again. Thank you, Ken. – Astin Mar 02 '15 at 07:59
  • I did like this, https://gist.github.com/AstinCHOI/9791f4b0ab941a2be6da. but It didn't work. Thanks for your help. – Astin Mar 03 '15 at 05:52
  • You left out the `mainMenu?.addItem(editMenuItem)` statement. – Ken Thomases Mar 03 '15 at 05:55
  • The current gist has it commented out. Also, check that `mainMenu` is not `nil`. – Ken Thomases Mar 03 '15 at 06:32
  • As you expected, mainMenu is nil T.T – Astin Mar 03 '15 at 06:42
  • Oh, OK. Do you have anything in the menu bar? I've changed my answer to show code for creating the main menu of the app. The Edit menu is added to that before it is set on the app. – Ken Thomases Mar 03 '15 at 09:27
  • Sorry to response late. I've done with your code, but it didn't work. Thanks for answering my problem, Ken. – Astin Mar 08 '15 at 15:13
  • gist.github.com/AstinCHOI/9791f4b0ab941a2be6da. It doesn't work – Astin Mar 08 '15 at 17:43
  • I see `NSStatusItem` in there. Is your app just for a status item? Have you set `LSUIElement` in the Info.plist? (Might be presented as "Application is agent (UIElement)" in the property list editor.) If so, then it can't have a menu bar. That's the **purpose** of `LSUIElement`, to prevent your app from having a menu bar or a Dock icon. – Ken Thomases Mar 08 '15 at 18:22
  • That's right. My app just has a status item. The reason why I use NSMenu is to use "copy", "paste", "cut" and "selectAll" in the NSTextField. Thanks for helping this issue all the time. So, my new question is how to set LSUIElement or have an alternative way to use copy, paste, cut and select all without NSMenu? Thanks. T.T – Astin Mar 09 '15 at 00:15
  • You can add a custom view anywhere within the window that overrides `-performKeyEquivalent:` to check the key event for standard edit keyboard shortcuts. If it matches, it sends the appropriate action to the responder chain. This [question](https://stackoverflow.com/questions/26048241/obj-c-why-is-my-nstextfield-subclass-having-an-effect-on-objects-i-havent-assi) links to a third-party custom text field class which does that. As the question indicates, you only need to use that custom class for one text field in the window to handle the keyboard shortcuts for all controls in the window. – Ken Thomases Mar 09 '15 at 00:31
  • Always Thanks for your answer. Once I get the reputation, I will do your answer up. I will try it as you mentioned. Thank you :) – Astin Mar 09 '15 at 00:49
4

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/MenuList/Articles/EnablingMenuItems.html

Automatic Menu Enabling

  • If the menu item’s target is not set (that is, if it is nil—typically if the menu item is connected to First Responder) and the NSMenu object is not a contextual menu, then NSMenu uses the responder chain (described in The Responder Chain) to determine the target. If there is no object in the responder chain that implements the item’s action, the item is disabled. If there is an object in the responder chain that implements the item’s action, NSMenu then checks to see if that object implements the validateMenuItem: or validateUserInterfaceItem: method. If it does not, then the menu item is enabled. If it does, then the enabled status of the menu item is determined by the return value of the method.

So, in order to achieve Automatic Menu Enabling for standard system menu items we have to specify correct action without setting target.

let menu = NSMenu(...)
menu.addItem(withTitle: "Quit \(applicationName)",
             action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
menu.addItem(withTitle: "Select All",
             action: #selector(NSText.selectAll(_:)), keyEquivalent: "a")
menu.addItem(withTitle: "Copy",
             action: #selector(NSText.copy(_:)), keyEquivalent: "c")
Vlad
  • 6,402
  • 1
  • 60
  • 74
1

I found the answers here were generally in the right direction but something was missing, so I'll add my complete solution. In my case I wanted to add a "Debug" menu only for development.

func addDebugMenu() {
    let debugMenu = NSMenuItem(title: "Debug", action: nil, keyEquivalent: "")
    debugMenu.submenu = NSMenu(title: "Debug")
    debugMenu.submenu?.addItem(withTitle: "Load saved data", action: #selector(self.loadDataFromFile(_:)), keyEquivalent: "")
    NSApplication.shared.mainMenu?.addItem(debugMenu)
}

@objc func loadDataFromFile(_ sender: Any) {
    print("load it")
}

For the complete solution, call it like this for development-only:

#if DEBUG
addDebugMenu()
#endif

My environment is: Xcode 12.3, macOS 10.15.7, Swift 5.3.2

Jacob Mouka
  • 2,005
  • 18
  • 26