Reputation: 8675
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
Upvotes: 1
Views: 163
Reputation: 22948
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.
Upvotes: 2
Reputation: 413
How exactly do you fill the bound array at startup? Do you use NSArray or NSMutableArray? I don't know what you problem is, but the possible reasons for bindings not being updated:
[self.mutableArrayProperty addObject:@"Value"]
won't update bindings bound to mutableArrayProperty.Bindings rely on Key-Value Observing and they are updated when some value is set to bound property, not when returned by bound property object changes its state.
Upvotes: 1
Reputation: 3457
Basically the common step to follow for the table view are:
Put the tableview in your view controller ( through xib or programmatically ) and reference outlet to your view controller (eg: call it myTableView )
bind the delegate and datasource to your view controller ( through xib or programmatically)
(delegates in your view controller are UITableViewDataSource
, UITableViewDelegate
)
keep an array (maybe as properties of your view controller, let's suppose to call it NSMutableArray * mySource
) and fill up with some objects
(eg: this could be done in your viewDidLoad
if not yet populated)
implement the delegated methods of tableview like:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
that define the number of sections in your tableview (for now you could simply return 1 )
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
that should return your datasource array count (return [self.mySource count]
)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
that dequeue/create the cells to present into your myTableView
Once the UI views have been loaded call the method of your table view [self.myTableView reloadData]
(NB: i suggest you to call it at least in the -(void)viewDidAppear:(BOOL)animated
to be sure it will be really performed, or after pressing button in your view).
Then, each time you will add an item and you want to show the updated datsource in the tableview, simply call [self.myTableView reloadData]
again.
Hope it helps!
Upvotes: 1