Cory
Cory

Reputation: 2312

Programmatically bind a NSTableView

I trying to get a better understanding of cocoa bindings. I can get a basic Table working with a NSArrayController in IB builder. I'm using the same project and attempting to connect the bindings programmatically, however no rows are appearing.

This is my Header file

@interface SBGoalDetailController : NSViewController <NSTableViewDelegate, NSTableViewDataSource>

@property (nonatomic, strong) NSManagedObjectContext *gdcManagedObjectContext;
@property (nonatomic, strong) NSArrayController *accountArrayController;
@property (weak) IBOutlet NSTableView *accountTable;


- (id)initWithContext:(NSManagedObjectContext *)context;

And my implementation file

@implementation SBGoalDetailController

- (id)initWithContext:(NSManagedObjectContext *)context
{
    self = [super initWithNibName:@"GoalDetailView" bundle:nil];
    if (self) {
        [self setGdcManagedObjectContext:context];
    }
    return self;
}



- (void)awakeFromNib
{
    _accountArrayController = [[NSArrayController alloc] init];

    [[self accountArrayController] setManagedObjectContext:_gdcManagedObjectContext];
    [[self accountArrayController] setEntityName:@"Account"];
    [[self accountArrayController] setAutomaticallyPreparesContent:YES];
    [[self accountTable] bind:@"content" toObject:_accountArrayController withKeyPath:@"arrangedObjects" options:nil];

    [[self accountTable] bind:@"selectionIndexes" toObject:_accountArrayController withKeyPath:@"selectionIndexes" options:nil];
}


- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{

    NSView *returnView = [tableView makeViewWithIdentifier:@"AccountCell" owner:[tableView delegate]];

        NSTextField* textField = [[returnView subviews] objectAtIndex: 0];

    [textField bind: NSValueBinding
           toObject: returnView
        withKeyPath: @"objectValue.accountName"
            options: nil];

    return returnView;
}

Any suggestions on what step I'm missing?

Upvotes: 2

Views: 3246

Answers (2)

Cory
Cory

Reputation: 2312

Thanks Noa the ArrayController content was nil, then I stumbled upon this section Automatically Prepares Content flag, also heeded your stylistic points and change my awakeFromNib to the following...all seems to be working, Thanks.

- (void)awakeFromNib
{
    [self setAccountArrayController:[[NSArrayController alloc] init]];

    [[self accountArrayController] setManagedObjectContext:[self gdcManagedObjectContext]];
    [[self accountArrayController] setEntityName:@"Account"];

    NSError *error = nil;
    BOOL success = [[self accountArrayController] fetchWithRequest:nil merge:NO error:&error];
    if (success) {
        [[self accountTable] bind:NSContentBinding toObject:[self accountArrayController] withKeyPath:@"arrangedObjects" options:nil];

        [[self accountTable] bind:NSSelectionIndexesBinding toObject:[self accountArrayController] withKeyPath:@"selectionIndexes" options:nil];
    } else {
        NSLog(@"Error %@:", [error localizedDescription]);
    }
}

Upvotes: 3

paulmelnikow
paulmelnikow

Reputation: 17208

Simple things first: make sure that -awakeFromNib is called exactly once, and that _gdcManagedObjectContext and accountTable are non-nil at that time.

Try adding a static label or a background color to your view so you can confirm that the problem is no rows (vs. rows with invisible content).

When you confirm the problem is no rows, you can conclude there is a problem in -awakeFromNib. Try adding printouts of your array controller's arrangedObjects. It's probably empty. Your code in -tableView:viewForTableColumn:row in theory isn't being called yet. You could confirm that with a breakpoint or NSLog.

If that's the case, check where you're setting up your Core Data stack. Are you using NSPersistentDocument? I've encountered an issue where the run loop needs to run once before the managed object context will start working but I'd have to think through whether that's the problem you're seeing here.

There is an issue with your code in -tableView:viewForTableColumn:row, which is that you're potentially setting the binding over and over again. You should just do this once for each instance of the cell view. Even if you want to set up the array controller in code, I'd suggest that you consider binding the cell view's subviews in the nib, since it's all right there. Or, if you do it in code, you need to find a way to do it just once per view. I don't think that's causing your problem, though.

Stylistic points: in your code, use self.accountArrayController and self.gdcManagedObjectContext instead of _accountArrayController and _gdcManagedObjectContext. Also, you can use constants for the other binding types: NSContentBinding and NSSelectionIndexesBinding.

Upvotes: 3

Related Questions