Search code examples
objective-ccocoamacoscore-datacocoa-bindings

NSPopUpButton: multiple values & selectedIndex binding


Context:

I have an NSArrayController tied to Core Data that supplies rows for an NSTableView. When a user selects rows, the arrayController's "selectedObjects" property changes.

Now, each of those "selectedObjects" is a Core Data entity called "LPFile" that has an attribute called "style", which is an integer from 0 to 3. The "style" attribute should correspond to the selectedIndex of an NSPopUpButton.

My Question:

If a user selects multiple rows AND the LPFiles associated with these rows have the same value for "style", I would like the NSPopUpButton to set its "selectedIndex" property to that value. If the rows' objects have DIFFERENT values for "style", then the NSPopUpButton should display a blank row. (When the user then chooses a style, that blank row should disappear from the NSPopUpButton.)

I know how to achieve this by writing code manually and if selection was limited to a single row I could set up those bindings, but how do I set up the bindings to handle multiple selected objects that may or may not have different values for "style"? I've Googled quite a bit, but can't find specific info and I'm tired of experimenting! (Note: I provide the content items for the NSPopUpButton in IB, so I don't bind anything to the content bindings of the button.)


Solution

  • You'll probably have to write a little bit of code, but you can still use bindings to control the UI elements, in this case the popup button.

    Here is one way to do it that has worked for me:

    In the controller that provides the content for the array controller, define a property which contains the selection index set corresponding to the selection in the table view. Bind it to the array controller's selection index set, so it is always updated and sync'ed with the table view. For simplicity, I have called it fileSelectionIndexSet in the following.

    Then, define a property that provides the index for the popup button. Below, I have called it styleIndex.

    You can bind the popup buttons selection index to this property. You may have to provide its content from the controller, too. That would be a readonly property returning a static array of strings, for instance.

    // Header file, just synthezise in implementation
    @property (retain) NSInteger styleIndex;
    

    Register the controller as an observer of its own fileSelectionIndexSet property:

    // It doesn't have to be awakeFromNib, any method will do if called before
    // you need the functionality
    -(void)awakeFromNib
    {
        [self addObserver:self 
               forKeyPath: @"fileSelectionIndexSet" 
                  options:NSKeyValueObservingOptionNew 
                  context:NULL];             
    }
    
    
    - (void) observeValueForKeyPath:(NSString *)keyPath
                           ofObject:(id)object
                             change:(NSDictionary *)change
                            context:(void *)context
    {
        if ( [keyPath isEqualToString: @"fileSelectionIndexSet"] ) 
        {
            NSInteger index;
            index = ... // Compute value based on current LPFile selection
            self.styleIndex = index;
        }
    }
    

    Implementing self as an observer of its own property makes the styleIndex property a one-way dependant of the fileSelectionIndexSet.

    This means that whenever the user changes the selection in the table view, the popup button is updated. However, when the user changes the selection in the popup button, nothing is changed in the table view.