Change the null placeholder in a Cocoa binding?

Is there a way to change (for the purpose of localization) the null placeholder in a binding in Cocoa?

The bindings are set up in Interface Builder for a popup button. The two-way nature of the bindings as set up in IB is needed, so doing it programmatically is not really appealing.

I am aware that the standard way of handling localizations of a nib file is by making one for each language, but since this is the only difference in the whole nib file between the language versions, it seems a bit excessive for a single string.

If there is a way to modify a binding created in IB, I was thinking about doing it in the file's owner's awakeFromNib method.

Answers


In the controller object to which you bind, such as your NSDocument class, override -bind:toObject:withKeyPath:options:. This needs to be the target of that method invocation – the object you select under Bind to: in the nib.

If you bind to an NSObjectController or NSArrayController, you'll need a subclass.

That method should rewrite the options dictionary and invoke super, replacing the value for NSNullPlaceholderBindingOption with your localized string.

I would omit the null placeholder from the nib and that key value in code, though you could of course take the passed-in value for that key and translate it, instead.


The other answer no longer seems to work so I've come up with a slightly different solution which modifies an existing binding to use the given null placeholder string:

I have this method in my view controller:

- (void)rebind:(NSString *)binding of:(id)object withNullPlaceholder:(NSString *)nullPlaceholder {
    // Possibly a bad idea, but Xcode doesn't localize the null placeholder so we have do it manually.
    NSDictionary *bindingInfo = [object infoForBinding:binding];

    id bindObject = bindingInfo[NSObservedObjectKey];
    NSString *keyPath = bindingInfo[NSObservedKeyPathKey];
    NSMutableDictionary *options = [bindingInfo[NSOptionsKey] mutableCopy];
    options[NSNullPlaceholderBindingOption] = nullPlaceholder;

    [object unbind:binding];
    [object bind:binding toObject:bindObject withKeyPath:keyPath options:options];
}

I call this in awakeFromNib for all the bindings that need it and pass in a localized string:

- (void)awakeFromNib {
    // Hacky hack hack: Xcode is stupid and doesn't localize the null placeholders so we have to do it.
    [self rebind:@"contentValues" of:self.fooPopup withNullPlaceholder:NSLocalizedString(@"No foos available", @"foo popup null placeholder")];
    [self rebind:@"contentValues" of:self.barPopup withNullPlaceholder:NSLocalizedString(@"No bars available", @"bar popup null placeholder")];
}

The localized strings are then localized normally as part of the Localizable.strings file.


I was able to change the null placeholder string (i.e. "No Value") in a NSPopUpButton that uses bindings.

Specifically, I wanted to have an popup button menu item that had a title other than "No Value" with a represented object of nil. An empty NSString or nil should be saved in the user defaults when the null placeholder menu item is selected.

NSPopUpButton Bindings:

  • Content is bound to an NSArrayController.arrangedObjects

  • Content Objects is bound NSArrayController.arrangedObjects.exampleContentObject (NSString), this is the object that the menu item represents and is the Selected Object,

  • Content Values is bound to NSArrayController.arrangedObjects.exampleContentValue (NSString), this is the title shown in the popup button's menu item.

  • Selected Object of the popup button is bound to NSSharedUserDefaultsController.values.ExampleUserDefaultsKey which is the same object type as the Content Objects and Selected Object (NSString). This object should match the object type of the NSUserDefault's key specified in the binding. When an item from the popup button is selected, it will save the Selected Object to the user defaults.

To change the null placeholder string from "No Value" to something else, subclass NSPopUpButton and override -[NSPopUpButton bind:toObject:withKeyPath:options:].


@interface CustomPopUpButton : NSPopUpButton
@end

@implementation CustomPopUpButton
- (void)bind:(NSString *)binding toObject:(id)observable withKeyPath:(NSString *)keyPath options:(NSDictionary<NSString *,id> *)options {
    NSMutableDictionary *mutableOptions = options ? [options mutableCopy] : [NSMutableDictionary dictionaryWithCapacity:1];
    mutableOptions[NSInsertsNullPlaceholderBindingOption] = @YES;
    mutableOptions[NSNullPlaceholderBindingOption] = @"Custom Null Placeholder Text";
    [super bind:binding toObject:observable withKeyPath:keyPath options:[mutableOptions copy]];
}
@end

Finally, select the NSPopUpButton in Interface Builder and under Custom Class in the Xcode Identity Inspector to your the class to the NSPopUpButton subclass.


Need Your Help

Applet not running on web - RuntimeException

java exception applet runtime

I managed to deploy my applet using this tutorial