1

I have an NSPopupButton whose content is bound to an NSArray, let’s say the array is

@[
    @"Option 1",
    @"Option 2"
];

Its selected object is bound to User Defaults Controller, and is written to a preference file by the user defaults system.

In my code I check whether the preference is set to @"Option 1" or not, and perform actions accordingly.

This all worked well (though I did feel a little uneasy checking for what is essentially a UI value, but whatever...) until I needed to localize.

Because the value is the label, I’m having an issue.

If my user is in France, his preferences file will say @"L’option 1", which is not equal to @"Option 1". I need to abstract the presentation from the meaning and it's proving pretty difficult.

I split up the binding into two arrays, let's call them values and labels.

Let’s say they look like this:

values = @[
            @"option_1",
            @"option_2"
          ];

labels = @[
            NSLocalizedString(@"Option 1", nil),
            NSLocalizedString(@"Option 2", nil)
          ];

I’ve bound the NSPopUpButton’s Content binding to values and its Content Values binding to labels. However, the popup list is showing option_1 and option_2, it does not seem to want to use the labels array to label the items in the popup button.

How do I get the NSPopUpButton to use values internally and store that in the preferences file, but display labels to the user?

It doesn’t have to be architected this way, if you can think of a better solution. The point is I want to store and check one value, and have that value associated with a label that gets localized appropriately.

magiclantern
  • 768
  • 5
  • 19

2 Answers2

0

Cocoa bindings work very well with value transformers, because you can apply them directly in the bindings window, for example

@implementation LocalizeTransformer

+ (Class)transformedValueClass
{
    return [NSArray class];
}

+ (BOOL)allowsReverseTransformation
{
    return NO;
}

- (id)transformedValue:(id)value {

    if (![value isKindOfClass:[NSArray class]]) return nil;
    NSMutableArray *output = [NSMutableArray arrayWithCapacity:[value count]];
    for (NSString *string in value) {
        [output addObject:NSLocalizedString(string, nil)];
    }
    return [output copy];
}

@end

you have to register the transformer in awakeFromNib or better in +initialize

 NSValueTransformer *localizeTransformer = [[LocalizeTransformer alloc] init];
 [NSValueTransformer setValueTransformer:localizeTransformer
                                 forName:@"LocalizeTransformer"];

then it appears in the popup menu of value transformers

vadian
  • 274,689
  • 30
  • 353
  • 361
  • Thanks. This doesn't fully work, it does indeed replace the strings with the correct localized values, but it writes the transformed, localized strings to the preference files, so I’m back to square one in that regard. I could enable reverse transformation and perform a reverse lookup on the NSLocalizedString but that doesn't seem fully determinate. – magiclantern Jul 12 '15 at 22:29
  • In this case it might be more convenient to store user defaults in code rather than using bindings. – vadian Jul 13 '15 at 15:13
0

Bind Selected Tag to your User Defaults Controller instead of Selected Object.

If the NSPopupButton choices are fixed add the NSMenuItems in Interface Builder and set their Tags. Otherwise bind an array of NSMenuItem, again with proper Tags.

Selected Index would also work but only until you change the order.

John
  • 964
  • 8
  • 21