Saturday, September 26, 2009

Cocoa: TableView bindings---simplest possible example



This is without doubt the world's simplest NSTableView bindings example. To try it out, create a standard XCode Cocoa application and add the relevant items from this post to it.

The AppDelegate has an init method, which calls another function to load some preliminary data from a plist file. This has to happen before "awakeFromNib" or the TV won't display anything when it launches. I put the data in a plist file in resources for convenience, but you could easily construct it from scratch. I show the scratch version because the plist won't display properly in html:


- (id)init {
self = [super init];
if (self == nil) return nil;
[self loadArray];
return self;
}

- (void)loadArray{
data = [NSMutableArray arrayWithCapacity:10];
NSMutableDictionary *mD = [
NSMutableDictionary dictionaryWithCapacity:2];
[mD setObject:@"x" forKey:@"name"];
[mD setObject:@"1" forKey:@"uid"];
[data addObject:mD];
NSMutableDictionary *mD2 = [
NSMutableDictionary dictionaryWithCapacity:2];
[mD2 setObject:@"y" forKey:@"name"];
[mD2 setObject:@"2" forKey:@"uid"];
[data addObject:mD2];
[data retain];
}


If you wanted to load the data from a plist resource, you would do something like this instead:


- (void)loadArray{
NSString *fn = [NSString stringWithString:@"data"];
NSString *path = [[NSBundle mainBundle]
pathForResource:fn ofType:@"plist"];
data = [NSMutableArray arrayWithContentsOfFile:path];
[data retain];
}


The data variable is declared in the header as usual (no getters or setters or @property stuff):


#import <Cocoa/Cocoa.h>

@interface TVBindingsAppDelegate : NSObject {
NSWindow *window;
NSMutableArray *data;
}

@property (assign) IBOutlet NSWindow *window;

- (void)loadArray;
- (IBAction)report:(id)sender;

@end


The AppDelegate also has a method so I can see what has happened to the data source.


- (IBAction)report:(id)sender{
NSLog(@"data %@", [data description]);
}


That's all the code there is! The window of the running application, again:



In IB, drag an array controller onto the nib:



Here are the bindings. Be sure the table columns are selected when you bind to the array controller.





As shown in the screenshot, there is a Table View with two columns. Editing works, and the + and - buttons add new rows or delete the selected row. Connect the + and - buttons by control drag to the controller, and report to the App Delegate, as usual. Check the connections for the array controller.



Make sure the array controller knows the right class to add to the array:



One thing I don't know how to do yet is to change the default behavior, which only adds at the end, to add a new row after the selected row, say, at the second position. I suppose the way to do that is to connect the buttons to the App Delegate and modify the data source.