validateMenuItem: not called
Asked Answered
C

3

9

I'm facing a weird situation. I've got an NSMenu with a submenu in it. The submenu's contents are populated programmatically. In my validateMenuItem: method, I can see all items being checked (the parent's items) as well as the subitems (once I click on a submenu), except for those in my auto-populated submenu.

Why is that? Am I doing something wrong? Any ideas on how to fix that?

Coshow answered 3/3, 2013 at 10:24 Comment(5)
And these menu items operate normally otherwise (i.e. when you select them they fire their action method)?Semen
@Semen Just found the answer (pretty simple to be honest...) - I'll post it right now.Coshow
@Semen All of the menu items' actions were implemented in the controller, while that one specific submenu's subitems' actions were not. Thus, the validateMenuItem: method was not to be called. (Frustrating... given that I was struggling with it 2 hours before posting it in SO (and then answering it myself after 10 minutes... lol))Coshow
Just talking about a problem is often enough to allow you to solve it yourself.Semen
@Semen True. Weird but true..Coshow
C
19

Here is the solution :

Cocoa looks for the validateMenuItem: method in the Class where the NSMenuItem's action selector is.

So, if your NSMenuItem's action selector (e.g. @selector(someSelector:)) is implemented in SomeClass, then make sure you have a validateMenuItem: method in SomeClass too, if you want to validate the corresponding menu items.

Coshow answered 3/3, 2013 at 10:32 Comment(7)
Don't you target the First Responder with your action?Semen
@Semen Nope, I am not. All actions are linked to specific classes. Should I target the First Responder?Coshow
I guess it depends on the App, but in my Document Based App, I have all menus targeting First Responder I think.Semen
Well, First Responder is one of the things I've been tempted to use for quite a long time, but given (probably the control-freak part of my ... coding nature... lol) that I want to know 100% what-goes-where and what-does-what I haven't tried it so far (and perhaps it's not the best decision ever). Btw, the (quite huge) project I'm currently working on is indeed a Document-Based app too (but not in the strict Cocoa (see : NSDocument) sense...), so your input could be of some help. Thanks, mate! :-)Coshow
Id urge you to try first responder :) It helps quite oftenIzmir
Well I believe the real value of using the First Responder is the context-sensitive nature of the menu items. You only get one set of menu items, so you want things like Edit > Copy to relate to whatever is selected in the current document. This concept can be applied to most menu items I think.Semen
@Semen Well, the Edit menu is the one I'm most worried about. Since, guess what, my app is an editor (see: the one you'll most likely be using in a while ... lol) but not a very Cocoa Controls -compliant one either... So, actions - though seemingly the usual ones - don't correspond to their traditionally defined counterparts...Coshow
K
5

@Dr.Kameleon has the right answer.

I'll add one small point to update it if that's OK? My code broke recently in this area and stopped calling validateMenuItem: when it was working before. I didn't notice because the only symptom was a menu item no longer disabled when it should.

The issue was Swift 4. The method must be attributed with @objc. In earlier versions of Swift, inheriting from NSObject, NSViewController, etc. was enough but that changed with the newer versions of Swift, 4 and 5.

p.s. It seems it is fine to put the method in an extension.

Kienan answered 28/7, 2019 at 8:3 Comment(0)
S
0

The above answers did not help me to solve my problem. I have created a separate project to understand when the validateMenuItem(:) method is called.

The validateMenuItem(:) method will be called only if:

  1. Conform to NSMenuItemValidation in the class which implements the NSPopUpButton.
  2. All NSMenuItems must have an action and target set to the object which implements the NSMenuItemValidation protocol.
  3. Implement the validateMenuItem(:) method.
  4. Implement the dummyAction(:) method for NSMenuItem which doesn't do anything.
  1. The NSPopUpButton "Items: Autoenables" autoenablesItems must be set

Version 11.5 (11E608c) , Swift 5.0, DP: macOS 10.15.

Code:

import Cocoa
// 1) Conform to NSMenuItemValidation in the class which implements the NSPopUpButton.

class NSMenuItemValidationTestViewController: NSViewController, NSMenuItemValidation {
@IBOutlet weak var popupButton: NSPopUpButton!

// MARK: - ViewController lifecycle

override func viewDidLoad() {
    super.viewDidLoad()
    // .target and .action are set programmatically because menus are mostly build programmatically.
    // 2) All NSMenuItems must have an action and target set to the object which implements the
    // NSMenuItemValidation protocol.
    self.popupButton?.menu?.items.forEach{( $0.target = self )}
    self.popupButton?.menu?.items.forEach{( $0.action = #selector(dummyAction(_:)) )}
}

// MARK: - NSMenuItemValidation
// 3) Implement the validateMenuItem(:) method.
func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
    print("Function: \(#function), line: \(#line)")
    return true
}

// 4) Implement the dummyAction(:) method for NSMenuItem which doesn't do anything
@IBAction func dummyAction(_ sender: NSMenuItem?) {
    print("Function: \(#function), line: \(#line)")    }


}

// 5) The NSPopUpButton "Items: Autoenables" checkbox must be set to true in storyboard.
// or
// self.popupButton?.menu?.autoenablesItems = true

TODO: Github link to source code. (Coming soon).

Seizure answered 29/6, 2020 at 10:8 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.