PDA

View Full Version : NSPopUpButton Question...




blackenedheart
Sep 4, 2013, 07:32 AM
Hi,

I am new to this forum and I am asking my first question after searching for a solution and not finding an answer. Can anybody explain to me how they would set up a NSPopUpButton so that when a value is chosen from the dropdown list, and floating point variable is represented and chosen?

For example, let say I had an NSPopUpButton with 3 choices. The three items to choose from are Wine, Beer, and Vodka.

If I wanted Wine = 2.75, and Beer = 3.75, and Vodka = 4.75, how would most people set this up?

I am more familiar with iOS development and I am getting lost with the NSPopUpButton class.

Thanks in advance for any help I may recieve.



Peter Maurer
Sep 4, 2013, 08:25 AM
Two ideas come to mind: You could associate NSNumber instances to the popup button's menu items via objc_setAssociatedObject, or you could simply set tags (275, 375, and 475) and divide those by 100 when you do any calculations based on those numbers.

EDIT: Forgot the most obvious option, namely NSMenuItem's -setRepresentedObject: and -representedObject methods. Again, you could store NSNumbers that way.

blackenedheart
Sep 4, 2013, 01:51 PM
setRepresentedObject: is deprecated on OSX Lion. Can you give me an example of some code that might work instead?

chown33
Sep 4, 2013, 04:39 PM
Use tags. Then apply the general concept of mapping.

The input value is a tag (an integer). The output value is a float (or double). Here's an example mapping:
In -> Out
0 -> 2.75
1 -> 3.75
2 -> 4.75

The key concept is that the outputs can be anything you want. The input numbers are simply arbitrary tags attached to menu-items. Each menu-item's tag maps to a float or double value by going through a mapping object.

What class would make a suitable mapping object? Read this Apple article, then think it through:
http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Collections/Collections.html

If you aren't sure what to use, or you want further discussion, or confirmation of how to do the mapping, then post again.

I'm deliberately not posting code or an exact answer because you need to understand how mapping works in general, how to think about it, and how to use it to solve problems. You also have to know how the concept works in order to choose a suitable class. Not every mapping problem can be solved the same way.

EDIT
Also see:
https://developer.apple.com/library/mac/documentation/General/Conceptual/DevPedia-CocoaCore/Collection.html#//apple_ref/doc/uid/TP40008195-CH10-SW1

ArtOfWarfare
Sep 4, 2013, 04:48 PM
setRepresentedObject: is deprecated on OSX Lion. Can you give me an example of some code that might work instead?

Deprecated code still works - there's just no promise that it shouldn't break during an OS update.

Also, I personally stay away from tags - it seems to me that using them is a violation of MVC.

chown33
Sep 4, 2013, 04:57 PM
Also, I personally stay away from tags - it seems to me that using them is a violation of MVC.

It depends on how tags are used.

In the "divide by 100" usage, there's clearly Model or Controller data (the actual numbers scaled by 100) in the View. If different values were needed, then the View would need to change.

Using tags as keys to a mapping, and having the mapping belong to the Model or the Controller, then the View is just using an Adapter. That is, the mapping is a simplified Adapter pattern (http://en.wikipedia.org/wiki/Adapter_pattern). The need for an Adapter is driven by the limited representational capability of tags, i.e. they're ints, not objects.

mfram
Sep 4, 2013, 07:05 PM
I would just get the index of the selection out of the View and let the Controller figure out how to map the index of the selection to some useful value.

ArtOfWarfare
Sep 5, 2013, 05:11 AM
I would just get the index of the selection out of the View and let the Controller figure out how to map the index of the selection to some useful value.

This sounds better than tags, but still like a poor idea on account of the fact that shuffling the items in the list will mess this up.

I think I'd make a new class that stores the strings and values. I'd made several instances and stick them in an array. The method that displays the list queries the array and returns the string. The method that displays the value for the selected item queries the array and returns the value. Rearranging the data only has to take place in one spot, it allows for the potential to change your model to include more data later, and it doesn't make you rely on tags (which I honestly have no idea why exist...)

Peter Maurer
Sep 5, 2013, 06:42 AM
setRepresentedObject: is deprecated on OSX Lion. Can you give me an example of some code that might work instead?

Here you go:

#import "BeverageAppDelegate.h"
#import <objc/runtime.h>


@interface NSMenuItem (BeverageMenu)

- (void)BM_setAssociatedDouble: (double)theDouble;
- (double)BM_associatedDouble;

@end

@implementation NSMenuItem (BeverageMenu)

static const void *BMAssociatedDoubleKey = (void*)&BMAssociatedDoubleKey;

- (void)BM_setAssociatedDouble: (double)theDouble {
objc_setAssociatedObject(self, BMAssociatedDoubleKey, [NSNumber numberWithDouble: theDouble], OBJC_ASSOCIATION_RETAIN);
}

- (double)BM_associatedDouble {
return [objc_getAssociatedObject(self, BMAssociatedDoubleKey) doubleValue];
}

@end


@implementation BeverageAppDelegate

- (void)applicationDidFinishLaunching: (NSNotification*)theNotification {
NSPopUpButton *thePopUpButton = self.popUpButton;
NSMenu *theMenu = [thePopUpButton menu];
[theMenu removeAllItems];
[[theMenu addItemWithTitle: @"Beer" action: NULL keyEquivalent: @""] BM_setAssociatedDouble: 2.75];
[[theMenu addItemWithTitle: @"Wine" action: NULL keyEquivalent: @""] BM_setAssociatedDouble: 3.75];
[[theMenu addItemWithTitle: @"Vodka" action: NULL keyEquivalent: @""] BM_setAssociatedDouble: 4.75];
[thePopUpButton selectItemAtIndex: 0];
[thePopUpButton setAction: @selector(changeBeverage:)];
[thePopUpButton setTarget: self];
}

- (void)changeBeverage: (id)sender {
NSLog(@"associated double: %f", [[sender selectedItem] BM_associatedDouble]);
}

@end