4

If I have a method like:

@interface CharacterSet
    + (NSArray *)allCharacterSets;
@end

Can I bind to it using Cocoa bindings?

I'm trying to hook up an NSComboBox's content values to it. When I enter CharacterSet.allCharacterSets into the "Model Key Path" field in IB, it doesn't work, saying:

[ addObserver: forKeyPath:@"CharacterSet.allCharacterSets" options:0x0 context:0x200275b80] was sent to an object that is not KVC-compliant for the "CharacterSet" property.

I'm not sure what else to try. Currently I have to store the return value of allCharacterSets into an ivar in my custom window controller (or custom window) to make it work, which seems like an extra step I shouldn't have to take.

alexantd
  • 3,543
  • 3
  • 27
  • 41

2 Answers2

6

I'm working on a huge application and the extra steps you mentioned were really pain in the ass for me. So I developed a small proxy class that handles class method bindings.

First I've added a small proxy class

@implementation INClassProxy

- (id)valueForUndefinedKey:(NSString *)key {
    return NSClassFromString(key);
}

@end

Then added a category accessor to NSApplication

- (id)classProxy {
    static INClassProxy *proxy = nil;
    if (proxy == nil)
        proxy = [[INClassProxy alloc] init];
    return proxy;
}

(I actually added this to my application delegate and implemented application:delegateHandlesKey:)

Now you are ready to bind class methods to the Application object, even in the interface builder, with the keyPath @"classProxy.CharacterSet.allCharacterSets".

Alex Gray
  • 16,007
  • 9
  • 96
  • 118
cocoafan
  • 4,884
  • 4
  • 37
  • 45
  • 2
    Very clever and elegant solution. – alexantd Oct 06 '11 at 19:07
  • 1
    This is by far the best piece of bindings _hocus-pocus_ **ever invented**. Elaborating on perfection… go ahead and create a "generic, blue cube" `NSObject` instance in IB… then bind to your hearts content on that single object with as many "things" as you want… just alter the key path each time.. ie.. you want to bind an `NSPopupButton` to an `NSArray` that is the return to a class method on `NSColor` for instance… bind the button to `classProxy.NSColor.colorLists`. What's so fierce about this is that that single outlet is the gateway to ANY `NSObject`! – Alex Gray Apr 19 '13 at 00:09
1

@cocoafan.. I realized in my effusive comment.. that the example I stated may not work unless you were to actually implement your GENIUS answer as I had chosen to… In order to make this mind-numbingly brilliant (as well as simple) magic even more generic and useful… I just created a basic class as you had described, but instead of categorizing (is that what it's called?) NSApplication, i did it on NSObject. Full example below…

@interface AZClassProxy : NSObject
@end
@interface NSObject (AZClassProxy)
- (id)classProxy;
@end

@implementation AZClassProxy
- (id) valueForUndefinedKey:(NSString*)k { return NSClassFromString(k); }
@end
@implementation NSObject (AZClassProxy)
- (id) classProxy { static AZClassProxy *prx = nil; return prx = prx ?: AZClassProxy.new; }
@end

bound by class

Edit…  After like 2 days my simple mind folded trying to remember who this works, and why it's so wonderful.  I will keep posting here as I have to do things to remind myself of what this is, and why I should care..  So a basic example..

NSObject *WHATEVS = NSObject.new;
NSLog(@"%@", [[WHATEVS.classProxy valueForKey:@"NSColor"] redColor]);

LOGNSCalibratedRGBColorSpace 1 0 0 1

Alex Gray
  • 16,007
  • 9
  • 96
  • 118