I have several popup buttons whose selected tag is saved in the user defaults (by binding the selected tag in the Bindings inspector). Now instead of saving an integer I would like to save a string value (for the simple reason that it makes the user defaults more "readable" and failsafe), but unfortunately didn't find a way to bind a popup button's selected identifier. Is there a solution to this problem?
-
Bind the Selected Object. Did you bind the content of the popup button? – Willeke Nov 12 '19 at 10:24
-
I didn't bind the content of the popup button, I created all the items in the xib editor. Do you think that's the only way? – Nickkk Nov 18 '19 at 11:38
-
No, there are many ways but `NSPopUpButton` doesn't have a Selected Identifer binding or a `selectItem(withIdentifer:)` method. If you want to bind Selected Object you must set `representedObject` of the menu items in code or by binding Content Objects. – Willeke Nov 18 '19 at 15:12
1 Answers
The bindings for NSPopupButton
can be a little confusing. The various Content* bindings are used to provide the button with its list of possible selections. Content
itself is used to provide a list of objects represented by the items in the pop up button. Content Values
is used to provide the actual values displayed in the pop up button. For example, Content
might be bound to an array of model objects, while Content Values
is bound to a specific key path on those objects, for example name
, because you want to display the value of the name property of each item in the popup button itself.
Similarly, the bindings for selection correspond to this system. Selected Object
means that when a given item is selected, the underlying full object from the Content
array will be selected/set on the bound property, not just the simple displayed string (or number, etc.) value. On the other hand, Selected Value
will indeed bind just the displayed value.
Taken together, and in your case, where you're not using the content bindings, this means that you have two options:
- Bind the
Selected Value
to user defaults. - Create an underlying model class with both an
identifier
property and aname
(or whatever you want to call it) property. Bind theContent
binding to an array of those objects, and theContent Values
binding tothatArray.name
.
Option 1
This option is much simpler. Just set up the selected values binding and you're done. It has the major disadvantage that the actual displayed string is the thing being stored in user defaults. This means that if you change the wording of an item, that previously stored selection won't be restored, even if it corresponds directly to the newly-worded item. More importantly, it's not a good idea to make localized -- or potentially localized -- strings semantically important.
Option 2
This takes a little more work (and code), but it would solve your problem in a robust, "correct" way. For example:
@objcMembers class Option: NSObject {
dynamic var name: String
dynamic var identifier: String
init(name: String, identifier: String) {
self.name = name
self.identifier = identifier
}
}
class ViewController: UIViewController {
@objc dynamic var optionsForPopup = [Option(name: "Item A", identifier: "id 1"),
Option(name: "Item B", identifier: "id 2"),
Option(name: "Item C", identifier: "id 3")]
}
Bind:
Content
to ViewController -optionsForPopup
.Content Values
to ViewController -optionsForPopup.name
.Selected Value
to Shared User Defaults Controller - Controller Key:values
, Model Key Path:WhateverUserDefaultsKeyYouWant
.
Example
I've created an example project that implements option 2 here: https://github.com/armadsen/PopupDemo

- 21,309
- 5
- 56
- 97