3
votes

I'm trying to select and edit the name property of a newly added object.

On OSX, I have an array controller which displays its content in a table view. I have the table column's values connected (using bindings) through the AC's properties. Also, I set the table view's content and selectionIndexes bindings to point to the AC.

My subclassed AC has an IBOutlet to the table view (called tableView), and contains managed objects from a data model.

In my AC, I override the add: method.

- (void)add:(id)sender {
    [super add:sender];
    [[self managedObjectContext] processPendingChanges]; // no effect
    [tableView reloadData]; // no effect
    [tableView scrollRowToVisible:[[self arrangedObjects] count]-1];
}

The newly added object appears in the table view in a selected state (I have the AC set up in IB to select newly inserted objects). But the table view scrolls down to the second last row, putting the new row just outside the visible view.

When I try this,

[tableView scrollRowToVisible:[tableView selectedRow]];

or this,

[tableView scrollRowToVisible:[self selectionIndex]];

it gets even worse: the selectionIndex does not seem to be updated correctly.

- (void)add:(id)sender {
    [super add:sender];
    NSLog(@"selectionIndex = %lu", [self selectionIndex]);
    NSLog(@"number of objects in AC = %lu", [[self arrangedObjects] count]);
}

Logging the selectionIndex reveals that it always shows the previous selection index. Logging the number of objects in the AC is always one too little.

Am I trying to manipulate the table view too early? Any ideas which method would be better suited to override?


About the editing part..

The following statement interferes with the above scrollRowToVisible: method, as the last argument seems to select the row as well.

[tableView editColumn:0 row:0 withEvent:nil select:YES];

Anyway, the specified field (for testing purposes the first row of the table) seems to go into editing mode for just a flicker of an instant, but then finishes editing immediately.

Any help would be greatly appreciated.

2
is there no way to fix this with bindings? A NSArrayController has a binding to auto-select new inserts. Can't an NSTableView be binded, to automatically start editing a cell?Fnord23

2 Answers

2
votes

One solution I came up with:

- (void)rearrangeObjects {
    [super rearrangeObjects];
    [tableView editColumn:0 row:[self selectionIndex] withEvent:nil select:YES];
}

The last argument select:YES does not scroll the table to the row, nor does it select the row. The following line scrolls the table view as well.

[tableView editColumn:0 row:[self selectionIndex] withEvent:nil select:NO];

select:YES selects the text in the text field that is being edited.

Still, I am wondering if I am going correctly about this. This solution requires every array controller to have an outlet to the table view that displays its data. This seems redundant since the table view (and its columns) have been binded to the AC already.

Trying to implement MVC design pattern, shouldn't the responsibility to edit a cell, be delegated to the table view?

1
votes

The problem is that the array controller doesn't add the new object immediately. The documentation for -[NSArrayController add:] says:

Beginning with Mac OS X v10.4 the result of this method is deferred until the next iteration of the runloop so that the error presentation mechanism (see Error Responders and Error Recovery) can provide feedback as a sheet.

I'm not aware of any elegant way to get around this. You can try bypassing the add: method by adding the object directly to the NSMutableArray that the array controller is bound to. Alternatively, you can try use performSelector:withObject:afterDelay: with a short delay like 0.1 seconds, at which point the object should have been added.