Adding "Not Selected" Option To Bound NSPopupButton
I have an NSPopupButton that is bound to a core data backed NSArrayController. The selection of the NSPopupButton is also bound to a core data backed item. Bindings pretty much as follows:
NSArray 'A' of core data entities --> Array Controller Array Controller --> NSPopupButton.contentValues.arrangedObjects.name NSArray 'B' of core data entities --> Array Controller Array Controller --> NSPopupButton.selectedObject.methodOnObject.name
The selectedObject has a method on it that looks up an ID that finds an object and returns that object as follows:
-(MyWidget *)selectedWidget {
return [MyCunningLookupMethod widgetForID:[[self widgetID] intValue]];
}
And setting the object the other way is as simple as:
-(void)setSWidget:(MyWidget *)anObj {
[self setWidgetID:[anObj uid]];
}
Most of the time the object matches an object in the available contentValues list. However, there are times when the ID of the selected object is 0 - in which case I want to display an option in the available list called "Not Selected". I could easily accomplish sending back a different object (or nothing) if the object ID is 0.
开发者_StackOverflow社区I can handle this with a "No selection placeholder", but as soon as the user selects one of the other items, the placeholder for not selected is removed from the list.
I want to be able to provide the user with the ability to choose an item, or choose not to have an item selected (i.e. set it back to "Not Selected"). Short of creating the entire NSPopupMenu programmatically by walking the array I get from core data each time the selection changes, is there a way to insert a menu item into the list that represents a not selected state that will always be available to the user?
I have considered adding an entity object into the core data store that has all 0-based values with the exception of the name which would say "Not Selected". This, however, just doesn't feel like the right way to do things and actually presents some other issues about having empty objects in the store that don't actually have any data relevance.
As always, any and all help would be much appreciated.
SOLUTION
Well I didn't exactly follow what Hobbes the Tige
posted, but it pretty much put me where I needed to be. Instead of binding in IB I ended up creating a method that allows me to send in my array of objects on a selection change to a parent array or user activity initiating a change. The method then simply updates the NSPopupButton with the appropriate core data entity information and still allows me to us IB to bind the selectedTag of the matching object.
Here's the method
+(void)createContentsForPopup:(NSPopUpButton *)aPopupButton
withArray:(NSArray *)anObjectArray
addNotSelected:(BOOL)aFlag {
[aPopupButton removeAllItems];
if (aFlag) {
if (!anObjectArray || [anObjectArray count] == 0) {
[aPopupButton addItemWithTitle:@"--- Not available ---"];
[[aPopupButton itemAtIndex:0] setTag:0];
return;
}
[aPopupButton addItemWithTitle:@"--- Not selected ---"];
[[aPopupButton itemAtIndex:0] setTag:0];
}
NSSortDescriptor * sd = [NSSortDescriptor sortDescriptorWithKey:@"name"
ascending:YES];
[anObjectArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:sd]];
for (id anObject in anObjectArray) {
[aPopupButton addItemWithTitle:[anObject name]];
int thisItem = [aPopupButton indexOfItemWithTitle:[anObject name]];
[[aPopupButton itemAtIndex:thisItem] setTag:[[anObject uid] intValue]];
}
}
Obviously this depends upon any array of objects being passed in conforming to having a name
for the descriptive field and a uid
for the unique identifier.
Job done. :-)
Not saying this is the best solution, but it's an idea (you'll probably have to tweak it).
Add a menu item with title:
- (void)insertItemWithTitle:(NSString *)title atIndex:(NSInteger)index
So, like this:
[popupButton inserItemWithTitle:@"No selection" atIndex:0];
Putting it at index 0 will put it at the top. If you want it at the bottom of the list, use this method:
- (void)addItemWithTitle:(NSString *)title
Then when you get the action event, you can check for the item at index 0. If it's at index 0, you know it's the "No Selection."
-(IBAction)popupSelectionAction:(id)sender {
if (sender == popupButton) {
//Check for "No selection"
if ([popupButton indexOfSelectedItem] != 0) {
//Update core data managed object value
}
}
}
Or, if you use the addItemWithTitle, check if it's the last item in the array of items.
-(IBAction)popupSelectionAction:(id)sender {
if (sender == popupButton) {
//Check for "No selection"
if ([popupButton indexOfSelectedItem] != ([popupButton numberOfItems]-1)) {
//Update core data managed object value
}
}
}
Class reference:
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSPopUpButton_Class/Reference/Reference.html
精彩评论