I started learning about cocoa binding today and have successfully got it to work with one exception. That is, when I fill the bound array at startup, the tableview does not reflect this data until I add a new entry from the UI, at which point it will show the rest of the data that was in the array.
Do I need to add some code to 'refresh' the tableview after filling the array it is bound to?
Edit: here is my load method called from awakeFromNib
:
- (void)load
{
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *savedArray = [currentDefaults objectForKey:@"components"];
if (savedArray != nil)
{
NSArray *oldArray = [NSKeyedUnarchiver unarchiveObjectWithData:savedArray];
if (oldArray != nil) {
_components = [[NSMutableArray alloc] initWithArray:oldArray];
} else {
_components = [[NSMutableArray alloc] init];
}
}
}
Where _components
is the bound source for the NSTableView
One way to have the table populated at launch would be to call load
from within your init
method rather than waiting until awakeFromNib
. For example:
- (id)init {
if ((self = [super init])) {
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *savedArray = [currentDefaults objectForKey:@"components"];
if (savedArray != nil)
{
NSArray *oldArray = [NSKeyedUnarchiver unarchiveObjectWithData:savedArray];
if (oldArray != nil) {
_components = [[NSMutableArray alloc] initWithArray:oldArray];
} else {
_components = [[NSMutableArray alloc] init];
}
}
}
return self;
}
(Note you should change this to the designated init
method for the class you're using).
What is happening now is that before awakeFromNib
is called, the NSArrayController
is being created, and its initial content is being set to a nil
array (since _components
hasn't been created yet). Then awakeFromNib
is called, and you are creating _components
array, but in a way that is not key-value-observer-compliant (you are setting the instance variable directly, which doesn't allow the NSArrayController
to know that you've modified the array it's interested in).
By creating the _components
instance variable earlier on in init
, you assure that it will be available by the time the NSArrayController
is unarchived from the nib file. It can then set its initial contents to that array so there is data in the table view when the window appears.
Regarding your comment about how to modify _components
in a way that is KVC/KVO compliant, there are a couple of different ways, some of which are more efficient than others. You could call mutableArrayValueForKey:
like so:
[[self mutableArrayValueForKey:@"components"] addObject:theObject];
See Troubleshooting Cocoa Bindings: My collection controller isn't displaying the current data.
A more efficient way, though, is to add and remove items from the NSArrayController
itself rather than adding and removing from the underlying array. This lets the array controller know that changes are being made as well as letting the array controller handle modifying the underlying array's contents. See Programmatically Modifying a Controller’s Contents.