I have a NSPopupButton on my GUI and im looking for a way to programmatically change the text of a specific item:
'item 1'
'item 2' ---> 'item b'
'item 3'
The direct answer to your question is that you can access the items via NSPopUpButton
's menu
property, and then the items
property on the resulting menu. But don't stop reading here, because that's not the way I'd do it.
Most likely, the reason you want to change the menu item is to reflect the status of something in your model. Perhaps the title is context-sensitive and should change depending on the current selection, or something similar. You can often do this without needing an outlet to the NSPopUpButton
at all, simply by doing it in the menu item target's validateMenuItem
method:
override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
if menuItem.action == #selector(someAction:) {
if self.someCondition {
menuItem.title = "Foo"
} else {
menuItem.title = "Bar"
}
}
return super.validateMenuItem(menuItem)
}
Not only does this simplify your design and reduce the level to which your program logic needs to know about how your UI is set up, it's also more flexible, since it'll work for any menu item that points to the same target and action. If you decide to move the menu item out of the pop-up button and into the standard menu bar, a toolbar-based menu, or something else, or if you want to have multiple menu items in several of these places, they'll all work exactly the same way without you having to write any new code.
Another trick that I sometimes use is to populate the menu item's title via Cocoa Bindings. This also has the effect of decoupling the UI from the interface, but is a little more advanced of a topic that you might not want to jump into just yet.
EDIT: Now that I know your use case, that's exactly the sort of thing I tend to use Cocoa Bindings for :-) Something like:
class MyViewController: NSViewController {
...
// make these @objc and dynamic so that they are KVC-compliant
@objc dynamic var password: String
@objc dynamic var bits: Int
// register our password strength as dependent on 'password' and 'bits'
@objc private static let keyPathsForValuesAffectingPasswordStrength: Set<String> = [
#keyPath(password),
#keyPath(bits)
]
@objc var passwordStrength: String {
// obviously you'll do a better test than this in real life ;-)
if self.password.count >= 8 {
return NSLocalizedString("Strong", comment: "Strong")
} else {
return NSLocalizedString("Weak", comment: "Weak")
}
}
...
}
Basically, what's going on here is:
Everything is @objc
because Cocoa Bindings is written in Objective-C and needs to be able to see our properties.
Adding dynamic
to the password
and bits
properties allows Cocoa to stick magic into the accessors that make them fire notifications when the properties change.
The keyPathsForValuesAffectingPasswordStrength
property tells Cocoa that when the notifications get fired for password
or bits
, they should also fire for passwordStrength
.
Now, we just go to Interface Builder, and bind our secure password field to the password
property like this:
Then, we bind our slider to the bits
property like this:
And for our label, we get a bit fancier and set a pattern involving both passwordStrength
and bits
:
And so, with hardly any code except for a few property definitions, we get this:
Pretty slick, eh?