views:

251

answers:

2

The main view of my NSPersistentDocument based application is a table view (bound to an NSArrayController) showing the list of records, below it there is an "add record" button. I want the button to cause the following (supposedly trivial) behavior.

  1. Create an new object
  2. Set some defaults to the new object (that are stored in the main document and not available globally)
  3. Add it to the table view.

Here are the things that I tried or dismissed:

  1. use the NSArrayController "add" action - problem: will not return the new object and implementation is deferred so it is impossible to modify the newly created object
  2. Override the init of the data class - will not work - I need to access data that is stored in the document class instance
  3. Subclass NSArrayController and override "newObject" - again - will not work because I need to access data that is stored in the document.
  4. Following code "almost" worked:

    - (IBAction)newRecord:(id)sender
    {
        MyDataClass *newRecord = [recordsArrayController newObject];
    
    
    
    newRecord.setting1=self.defaultSetting1;
    newRecord.setting2=self.defaultSetting2;
    // ... etc...
    [recordsArrayController addObject:newRecord];
    [recordsTable scrollRowToVisible:[recordsTable selectedRow]];
    [newRecord release];    
    
    }

This code actually works well, for unsaved documents. But if I save the document and re-open it then clicking on the add button results in the new record showing twice in the table. Obviously the "addObject" is redundant (although it works fine in unsaved documents) but without it the new object is not selected.

A: 

Really, all you need to do is modify your code to omit the addObject: call. To make your new object selected, just do this:

[recordsArrayController setSelectedObjects:[NSArray arrayWithObject:newObject]];

before you do your call to swcrollRowToVisible:. You're right that the addObject: call is unneeded. As you've seen, it's ending up in the array controller twice.

Also, you won't need to call [newRecord release]. The documentation says the object is retained by the array controller. It's not failing now because it's being retained a second time when you do addObject:.

Alex
wrong, you do need to call `[newRecord release]`. Creating it with `-newObject` gives it a retain count of +1.
Mike Abdullah
Other than the argument over the retain count, this answer works and is a far better answer than subclassing NSArray.
Marcus S. Zarra
Thanks, the solution works. The comment regarding "relaese" is indeed not correct, newObject gives you a retained object which you need to release.
Eyal Redler
I was too hasty in my response. It turns out that this solution doesn't work.
Eyal Redler
Really? Because I tried it last night and it worked for me. Do you want to share your latest code?
Alex
Here's the code and I'm afraid that yes, it really doesn't work. Note that this is on Leopard. IMO, when I call setSelectedObjects the array controller hasn't picked up on the fact that the object was added yet and considers the object I pass as something that it doesn't contain. RegistrationRecord *newRecord = [recordsArrayController newObject]; /* setup the record */ [recordsArrayController setSelectedObjects:[NSArray arrayWithObject:newRecord]]; [recordsTable scrollRowToVisible:[recordsTable selectedRow]]; [newRecord release];
Eyal Redler
Yep, I think I steered you wrong a little bit. If you're using `newObject`, you'll still need to add the object to the array controller using `[recordsArrayController addObject:newRecord];` I'd do that after your step labelled `/* setup the record*/` Sorry for the confusion.
Alex
A: 

Simple case that should work:

MyDataClass *newRecord = [controller newObject];
// configure newRecord
[controller addObject:newRecord];
[newRecord release];

In order for the new object to be selected, the controller needs to have been configured for -setSelectsInsertedObjects:YES previously.

But, there's an alternative which I'd consider more proper. Subclass NSArrayController like so (slighty pseudo-code):

@interface MyRecordController : NSArrayController
@property id recordSetting1;
@property id recordSetting2;
@end

@implementation MyRecordController

@synthesize recordSetting1;
@synthesize recordSetting2;

- (id)newObject
{
    id result = [super newObject];
    newRecord.setting1 = self.recordSetting1;
    newRecord.setting2 = self.recordSetting2;
    return result;
}

@end

So, your code then becomes:

- (IBAction)newRecord:(id)sender
{
    recordsArrayController.recordSetting1 = self.defaultSetting1;
    recordsArrayController.recordSetting2 = self.defaultSetting2;
    [recordsArrayController add:self];    
}
Mike Abdullah
Why is this downvoted? Seems OK to me.
Rob Keniger