Search code examples
objective-cxcodecocoacocoa-bindings

Can't figure out how to do this cocoa binding


I am a .net developer that's trying to learn how to do cocoa programming on a mac. I have run into a problem that I can't seem to figure out or find an answer for.

I have created the following classes:

  • ITObject - subclass of NSObject
  • ITPlayer - subclass of ITObject
  • ITEnemy - subclass of ITObject

In my MainMenu.xib file I have a window that contains the following controls:

  • NSArrayController - linked to App Delegate (itObjects)
  • NSTableView - linked to a NSArrayController (arrangedObjects)
  • NSTextBox - linked to the same NSArrayController (selection.player)

itObjects is an NSMutableArray that I use to contain any ITObject classes. The NSArrayController was set to use ITObject as the class. When I add a ITPlayer to the itObjects property, the text box doesn't read the player property.

The player property is unique to ITPlayer. If the player property is not part of the class, I want to display 'N/A' in the text box, if it is there, I want the binding to show the value. Right now, it does nothing.

More information on the objects:

The code for the items looks like this:

@interface ITObject:NSObject <NSCoding>{ 
     int initiativeRoll, initiativeBonus, initiativeKicker;
}
@property int initiativeRoll, initiativeBonus, initiativeKicker;
@property (readonly) int initiativeTotal, initiativeCalc;
@end

That is the header file for the ITObject (obviously). The ITEnemy and ITPlayer are setup like this (only so far).

@interface ITPLayer:ITOjbect <NSCoding>{
     NSString *player;
}
@property (copy) NSString *player;
@end

The init method for the ITPlayer is setup in the implementation file like this:

- (void)init{
     self = [super init]
     if (self){
          player = @"undefined";
     }
     return self;
}

There is also a initWithCoder: method, but I haven't been using that yet, so I don't think that this is suspect.

Thank you very much for your help!


Solution

  • I typically do not recommend bindings, and definitely not complicated bindings, to new Cocoa developers. Bindings have the unfortunately situation of tending to "do nothing" when they are the least-bit misconfigured. This makes them very difficult to debug, even for experienced developers. Bindings work very well when dealing with a big page of configuration options tied to NSUserDefaults, but they're a pain to build a complicated UI out of.

    That to the side, your setup seems very odd. The owner of MainMenu.xib should almost always be a NSApplication. Instead of what you're doing, you should drag a new NSArrayController onto your XIB and wire it up as you've described, but not make it the file owner.

    I would expect that you're seeing exceptions in your log. Probably "NSArrayController does not respond to selector setDelegate:" or the like. If you're seeing this, it's because the default MainMenu nib file is configured to wire the application delegate to the file owner.


    First, just a little quote from the Apple docs to reinforce the point about bindings:

    Populating a view-based table view using Cocoa bindings is considered an advanced topic. While it requires significantly less code (in some cases no code at all), the bindings are hard to see if you are not familiar with the interface. It is sternly suggested that you are comfortable with the techniques for using view-based table views programmatically before you move on to Cocoa bindings.

    I love the word "sternly" in this section. After years of development with and without bindings, I tend to avoid them except in the simplest of cases (like preference panels, where they're incredibly useful).

    Without bindings, you would follow the instructions in Populating View-Based Table Views Programmatically. Basically, you will implement numberOfRowsInTableView: and (assuming 10.7+) tableView:viewForTableColumn:row:. For each row and column you return a view containing the data you want. This is taken from the iOS way of doing things and is very nice and extremely flexible.

    If you need pre-10.7, then things are slightly more complicated, but still more straightforward than bindings. See Populating Cell-Based Table Views. This uses NSCell rather than NSView. If you just need a simple table displaying string-like data, it's not hard. Just implement numberOfRowsInTableView: and tableView:objectValueForTableColumn:row:. The latter should return something that can be coerced into a string.

    If you need a custom NSCell for pre-10.7, try it out on your own using the docs, and then you'll probably have some new questions to post.