Set NSButton enabled based on NSArrayController selection

641 views Asked by At

OK, so I've set up an NSTableView bound to NSArrayController.

Now, I also have an NSButton which I want to make it "enabled" when there is a selection, and disabled when there is nothing selected.

So, I'm bind NSButton's Enabled to the Array Controller's selection with a value transformer of NSIsNotNil.

NSIsNotNil Array Controller and NSButton

However, it doesn't seem to be working.

Am I missing anything?

2

There are 2 answers

2
Paul Patterson On BEST ANSWER

Regardless of whether or not anything is selected, the selection property of NSArrayController returns an object (_NSControllerProxyObject). This is why your binding isn't working the way you expect, because selection will never be nil. Instead, I bind to selectionIndexes, rather than selection, and have a value transformer called SelectionIndexesCountIsZero implemented like so:

@interface SelectionIndexesCountIsZero : NSValueTransformer
@end

@implementation SelectionIndexesCountIsZero

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

+ (BOOL)allowsReverseTransformation { return NO; }

- (id)transformedValue:(NSIndexSet *)value { return [NSNumber numberWithBool:[value count] > 0]; }

@end

Incidentally, you can still bind to selection if you wish, but it will require a custom value transformer. Apple state that: If a value requested from the selection proxy [object] using key-value coding returns multiple objects, the controller has no selection, or the proxy is not key-value coding compliant for the requested key, the appropriate marker is returned. In other words, to find out if there is in fact no selection, you need to (i) get access to the proxy object, (ii) call one of the methods of your actual objects, and (iii) test to see if the return value from (ii) is NSNoSelectionMarker. Doing it this way the key method of your value transformer would look like this:

- (id)transformedValue:(id)selectionProxyObject {
    // Assume the objects in my table are Team objects, with a 'name' property
    return [selectionProxyObject valueForKeyPath:@"name"] == NSNoSelectionMarker ? @YES : @NO;
}

selectionIndexes is the better way since it is completely generic. In fact, if you do this sort of thing a lot it can be a good idea to build up a transformer library, which you can then just import into any project. Here are the names of some of the transformers in my library:

SelectionIndexesCountIsZero
SelectionIndexesCountIsExactlyOne
SelectionIndexesCountIsOneOrGreater
SelectionIndexesCountIsGreaterThanOne
// ...you get the picture
1
Tan Tom On

I bind to selectedObjects.@count, rather than selection