Search code examples
cocoainterface-builderkey-value-observingnsarraycontrollernsbutton

How to bind NSButton enabled state to a composed condition


This is my situation in Xcode Interface Builder:

IB situation

There is also an NSArrayController in entity mode which controls the content of the NSTableView. I want to enable the 'Create' button when the NSTableView is empty (as controlled by the NSSearchField) AND when the text in the NSSearchField is not empty. How do I achieve that? Is it possible without programming?

To what KVO compliant values can I bind the 2 enabled conditions of the 'Create' button?


Solution

  • I don't think there's a way to do it entirely in interface builder, but with a small amount of code you can get it working pretty easily. First, make sure your controller (or App Delegate) is set as the delegate of the search field, and that it has IBOutlet connections to the search field, the button and the array controller. Here's how I would implement it:

    // This is an arbitrary pointer to indicate which property has changed.
    void *kObjectsChangedContext = &kObjectsChangedContext;
    
    - (void)awakeFromNib {
    
        // Register as an observer so we're notified when the objects change, and initially at startup.
        [arrayController addObserver:self
                          forKeyPath:@"arrangedObjects"
                             options:NSKeyValueObservingOptionInitial
                             context:kObjectsChangedContext];
    }
    
    // This updates the button state (based on your specs)
    - (void)updateButton {
    
        BOOL canCreate = (searchField.stringValue.length > 0 &&
                          0 == [arrayController.arrangedObjects count]);
        [createButton setEnabled:canCreate];
    }
    
    // This delegate method is called whenever the text changes; Update the button.
    - (void)controlTextDidChange:(NSNotification *)obj {
        [self updateButton];
    }
    
    // Here's where we get our KVO notifications; Update the button.
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    
        if (kObjectsChangedContext == context)
            [self updateButton];
    
        // It's good practice to pass on any notifications we're not registered for.
        else {
            [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        }
    }
    

    If you're new to bindings some of that may look like Greek, hopefully the comments are clear enough.