Retrieving Data in A NSTableView - objective-c

I have a three column Table View populated by two NSMutableDictionaries which share the same keys (ie key | value1 | value2 ) with dict1(key,value1) and dict2(key,value2).
I want to manually enter data in the third column and create the key/value objects in dict2. But when I do that, my code picks the wrong key :S
Here's the code for the delegate :
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
if ([[aTableColumn identifier] isEqualToString:#"value2"])
{
[dict2 setValue:anObject forKey:[[[aTableView tableColumnWithIdentifier:#"key"] dataCellForRow:rowIndex] stringValue ]];
}
}
Any idea ?
EDIT : I want to complete my question : how can you retrieve the data displayed in a cell ?
I see no other way to use NSMutableDictionnaries with TableViews, because [dict allKeys] does not give the keys in the same order that the one that is used to display it ! And stocking a version of [dict allKeys] just seems redundant and stupid.

I think this delegate function you're using is used for showing the data in the tableview with the format data source. It's used for setting, not retrieving.
You may use - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath to set the data source for tableview.

Related

Adding object to a column in a cell-based NSTableView?

I'm having trouble with Columns in NSTableview. I want to add an item to a specific column in an instance of NSTableView. (Note that data is a pointer to an instance of NSMutableArray, which is a property of AppDelegate, which is the dataSource)
-(IBAction)addTask:(id)sender
{
[self.data addObject:#""];
[self tableView:_tableView setObjectValue:#"Hello World" forTableColumn: self.column row:0];
[self.tableView reloadData];
}
A tutorial I went through showed me how to edit a single-column NSTableView. However, using the same implementation of the NSTableViewDataSource methods causes tableView to populate both columns. The pertinent implementation from the tutorial:
-(void)tableView:(NSTableView *)tableView
setObjectValue:(id)object
forTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row
{
[self.data replaceObjectAtIndex:row withObject:object];
}
I reason part of the issue is that the above method doesn't do anything with the tableColumn parameter, but I'm still learning and have no idea how to proceed. (It's especially hard to find help because many modern tutorials involve view-based NSTableViews, and well I know that is best practice, I don't want to run away from this.) I hope my explanation was clear enough, and any help would be much appreciated.
The reason you are seeing the data populated in multiple columns is you are not differentiating what values go in what columns.
You should think of each item in your array as a row in your table view. If you wish to display different values in columns of that row you need a way to store those different values in the data source object. This can be done by using a concrete model object class that has multiple properties defined or by using a NSDictionary with different keys with their corresponding values.
For adding a row:
-(IBAction)addTask:(id)sender
{
NSMutableDictionary *newRow = [NSMutableDictionary dictionary];
[newRow setObject:#"Hello World" forKey:#"salutation"];
[self.data addObject:newRow];
[self.tableView reloadData];
}
So for the display code:
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
NSDictionary *row = [self.data objectAtIndex:rowIndex];
NSString *columnIdentifier = [aTableColumn identifier];
return [row objectForKey:columnIdentifier];
}
You'll notice we are using the column identifier here. Normally you set this value in Interface Builder on the table column. In the case above the identifier needs to be the same as the key in the dictionary for which you wish to display information.
Finally allowing the user to set a value of a column in the table view:
-(void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger) rowIndex
{
NSDictionary *row = [self.data objectAtIndex:rowIndex];
NSString *columnIdentifier = [aTableColumn identifier];
[row setValue:anObject forKey:columnIdentifier];
}

Reordering UITableViewCells with Core Data TableView

I have a UITableView that is working with Core data for the user o add their own Cells... but i wanted them to be able to reorder the TableViewCells the way they wanted... i use the code now BUT whenever the reorder it, say i went to add another cell, it would return the the regular state... images below if you're lost...
Below is the code:
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) // Don't move the first row
return NO;
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{
id ob = [_devices objectAtIndex:destinationIndexPath.row];
[_devices replaceObjectAtIndex:destinationIndexPath.row withObject:[_devices objectAtIndex:sourceIndexPath.row]];
[_devices replaceObjectAtIndex:sourceIndexPath.row withObject:ob];
}
You should add an order property on your entity so that your sort order is persistent.
Then your data source for the table view needs to be sorted by that order key.
It is likely that your _devices array is getting reinitialized with the results of your Core Data fetch request and therefore your modification to the order is lost.
Updated for comment
Assuming you have an entity named Device that is a sub-class of NSManagedObject with a property named order you could do the following.
NSSortDescriptor *orderSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"order" ascending:YES];
[devicesFetchRequest setSortDescriptors:#[orderSortDescriptor]];
Then when you execute your fetch request and capture the results in your _devices array they will be sorted by the order property.
Then in - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath you will need update the order property on the affected entities.
If you move the device from position 5 to position 2 then you need to update the entities from position 2-4 by +1 and the moved entity to 2. This could get inefficient for large data sets quickly but for small data sets it should perform fine.

Populating NSTableView from multiple NSArray records in one go

If I have a one to many relationship, how would I go about populating the NSTableView with such data that is found in an NSArray?
Say I have a data item called "Alphabet", and I want that when a user selects it out of a list, the return value(s) in :
-(id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
are the letters of the alphabet in different rows, one underneath the other.
Can that be done?
Thanks!
You need to either assign an object as the table view's datasource and have that object conform to the NSTableViewDataSource protocol, or you need to use an NSArrayController and Cocoa bindings.
If you use a datasource, implement the numberOfItemsInTableView: method and return the count of your array. You must also implement -tableView:objectValueForTableColumn:row::
-(id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
if(rowIndex > -1)
{
return [yourArray objectAtIndex:rowIndex];
}
return nil;
}

Obtain a stringValue from NSTableView

I have a simple NSTableView which I have loaded with data from a NSMutableArray. When I select a row (entry) in the tableView and modify it, I can find the row index, what I cannot get is the edited entry out as a string so I can modify the array. I can find lots of information on selecting rows, etc., but not on how to get the actual modified string. I keep thinking this should be real simple. Help please.
Part of my code:
- (IBAction)toDoEdit:(id)sender // Accept the edited data
{
NSString *toDoItem = [[toDoTableCell:toDoTableView dataCellFoTableColumn:0 row:rowToBeEdited] stringValue];
// I get the error "dataCellForTableColumn' method cannot be found.
[toDoArray replaceObjectAtIndex:rowToBeDeleted withObject:toDoItem];
[toDoTableView reloadData];
[toDoTableView deselectRow:rowToBeDeleted];
}
~~~~~~~~~~~
// This method should return the cell value of the selected row
- toDoTableCell:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
return toDoTableCell; // No errors here but toDoTableCell is nil.
}
The 'Add' data to tableView works, 'Delete' data from tableView works, I just cannot get the edited data out of tableView so I can reload the data with the corrections.
What you are looking for is an NSTableView Delegate method:
- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
This will return the NSCell that is in the row and column that you specify. From the NSCell you should be able to extract the value that you need. Depending on how you are using your NSCell you would either call [cell stringValue] or [cell objectValue].
Try this:
– tableView:setObjectValue:forTableColumn:row:
in - NSTableViewDataSource Protocol Reference
--- Edited based on comment ---
Above method is called whenever user tries to edit a table row, it also provides user with changed value as parameter. If you are trying to edit the row in table itself then it should serve your purpose. You can simply check the objectValue obtained as parameter and verify if it is correct or not. In case it is incorrect you can modify the obtained value and set it in todoArray.
Briefly:
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
// below case is an example, you can add your own
if([anObject isEqualToString:#"Incorrect"])
{
anObject = #"Correct";
}
// Considering todoArray is array of dictionary items containing keys as table-column identifiers
NSMutableDictionary *originalData = [todoArray objectAtIndex:rowIndex];
[originalData setValue:anObject forKey:[aTableColumn identifier]];
[toDoTableView reloadData];
}
To get the value being edited you can simply use this code in above method, before setting the new value:
NSString *editedValue = [[todoArray objectAtIndex:rowIndex] valueForKey:[aTableColumn identifier]];
Hope this helps :)
It is simple. Read up on Cocoa Bindings and NSArrayController.
Take a look at the NSTableView methods selectedColumn, selectedColumnIndexes, selectedRow and selectedRowIndexes. I guess they should provide you with the needed information.
Now you can query the model, i.e. the array, for the data you need.

Obj-C, have a number of tableviews, need to quickly add code to say not data found?

I have a number of views with UITableViews and I'd like to quickly add code to each so that I can add a row with a simple label saying that there was no data to be shown.
Whats making things a little bit more complicated is that in some cases I'm using custom cells.
I don't want to have to change my populate data array functions, but I can't think of a way to add this feature to a lot of tables quickly.
Can anyone show me what to do ?
In the tableView:numberOfRowsInSection: data source method, get the number of rows as normal. Then, if the number is 0, return 1 instead. In the tableView:cellForRowAtIndexPath: method, perform the same test. If there is no data, use a different cell and set it up for your message.
The following example assumes that you have 1 section and that your data is an NSArray instance variable named theArray which contains strings.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger num = theArray.count;
return (num ? num : 1);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
if(theArray.count == 0) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
cell.textLabel.text = #"No Data Found";
return cell;
}
// Normal processing here
}