Identify modified row for NSTableView bound with NSArrayController

36 Views Asked by At

I have an NSTableView bound to an NSArrayController, and have registered an observer for whenever data changes to the data fields that the array controller manages. For example, here's a few of the addObserver calls:

    [_arrayController addObserver:self forKeyPath:@"arrangedObjects.userName"     options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    [_arrayController addObserver:self forKeyPath:@"arrangedObjects.enabled"      options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    [_arrayController addObserver:self forKeyPath:@"arrangedObjects.lockSettings" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

After that, whenever data is changed by editing the table, my observer gets called just fine.

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if ([keyPath compare:@"arrangedObjects.userName"] == NSOrderedSame)
    {
        // Great, we know a userName changed somewhere in the table, but which one?
    }
}

The problem is, I need to know which one of the arranged objects triggered this. If I knew the tableview row that would work as well. It's nice to know that some userName changed somewhere, but I really need to know which one of the potentially hundreds caused the observer to be called.

Sorry for the old Objective C format of this, but some of us still have tons of legacy code to deal with.

1

There are 1 best solutions below

1
Willeke On BEST ANSWER

View-based table view

Connect the text fields in the cells to an action method of the delegate of the table view. Get the row with NSTableView method rowForView: and the column with columnForView:.

- (IBAction)editAction:(id)sender {
    NSInteger row = [self.tableView rowForView:sender];
    NSInteger column = [self.tableView columnForView:sender];
    NSLog(@"Edited row: %ld, column: %ld", (long)row, (long)column);
    …
}

Cell-based table view

Implement NSControlTextEditingDelegate method controlTextDidEndEditing: on the delegate of the table view. The edited row and column are editedRow and editedColumn of the table view.

- (void)controlTextDidEndEditing:(NSNotification *)aNotification {
    NSInteger row = self.tableView.editedRow;
    NSInteger column = self.tableView.editedColumn;
    NSLog(@"Edited row: %ld, column: %ld", (long)row, (long)column);
    …
}

Key

There are several ways to get the key:

  • Set the identifier of the column (and text field in case of a view based table view) in IB to the key.
  • Get the key from the value binding:
NSString *key = [[tableColumnOrTextField infoForBinding:NSValueBinding] objectForKey:NSObservedKeyPathKey]; // something like "arrangedObjects.name"
NSRange aRange = [key rangeOfString:@"."];
if (aRange.location != NSNotFound)
    key = [key substringFromIndex:aRange.location + 1];
  • Get the key from the sort descriptor of the column
  • Add outlets for the columns

Bindings solution (no coding required)

Edit the values in a detail view. Bind the text fields and other controls to controller key selection of the array controller. "Allowes Editing Multiple Values Selection" is switched on by default. Works for cell-based and view-based table views.