How to do an on-item-changed for an NSPopUpButton?

2.4k views Asked by At

I'm trying to implement a system that changes a label based on the state of an NSPopUpButton.
So far I've tried to do what's displayed in the code below, but whenever I run it, the code just jumps into the else clause, throwing an alert

- (IBAction)itemChanged:(id)sender {
    if([typePopUp.stringValue isEqualToString: @"Price per character"]) {
        _currency = [currencyField stringValue];
        [additionalLabel setStringValue: _currency];

    }
    else if([typePopUp.stringValue isEqualToString: @"Percent saved"]) {
        _currency = additionalLabel.stringValue = @"%";
    }

    else alert(@"Error", @"Please select a calculation type!");
}

So does anyone here know what to do to fix this?

2

There are 2 answers

10
trojanfoe On BEST ANSWER

@hamstergene is on the right track, but is comparing the title of the menu item rather than, say, the tag, which is wrong for the following reasons:

  1. It means you cannot internationalize the app.
  2. It introduces the possibility of spelling mistakes.
  3. It's an inefficient comparison; comparing every character in a string takes way longer than comparing a single integer value.

Having said all that, NSPopUpButton makes it difficult to insert tags into the menu items, so you need to use the index of the selected item:

Assume you create the menu items using:

[typePopUp removeAllItems];
[typePopUp addItemsWithTitles: [NSArray arrayWithObjects: @"Choose one...", @"Price per character", @"Percent saved", nil]];

Then create an enum that matches the order of the titles in the array:

typedef enum {
    ItemChooseOne,
    ItemPricePerCharacter,
    ItemPercentSaved
} ItemIndexes;

And then compare the selected item index, as follows:

- (IBAction)itemChanged:(id)sender {
    NSInteger index = [(NSPopUpButton *)sender indexOfSelectedItem];
    switch (index) {
    case ItemChooseOne:
        // something here
        break;
    case ItemPricePerCharacter:
        _currency = [currencyField stringValue];
        [additionalLabel setStringValue: _currency];
        break;
    case ItemPercentSaved:
        _currency = @"%";               // See NOTE, below
        additionalLabel.stringValue = @"%";
        break;
    default:
        alert(@"Error", @"Please select a calculation type!");
    }
}

NOTE the following line was incorrect in your code:

_currency = additionalLabel.stringValue = @"%";

Multiple assignment works because the result of x = y is y. This is not the case when a setter is involved. The corrected code is above.

EDIT This answer was heavily edited following more info from the OP.

0
hamstergene On

To query the title of currently selected item in NSPopUpButton:

NSMenuItem* selectedItem = [typePopUp selectedItem];
NSString* selectedItemTitle = [selectedItem title];

if ([selectedItemTitle isEqualTo: ... ]) { ... }

Note that comparing UI strings is a very bad idea. A slightest change in UI will immediately break your code, and you are preventing future localization. You should assign numeric or object values to each item using -[NSMenuItem setTag:] or -[NSMenuItem setRepresentedObject:] and use them to identify items instead.