Reordering UITableViewCells with Core Data TableView - objective-c

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.

Related

Retrieving Data in A NSTableView

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.

One section per row in a UITableView

I am trying to create a UITableView that has one row per section to give the look of a message chat table. How would one achieve this? Using
[array count]
In different combinations in the two delegate methods below either return separate sections of the same cell or multiple rows in each section.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [array count];}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;}
That looks like it should do what you want. Then you need to use the section property from the index path instead of the row when picking out an array element in cellForRowAtIndexPath:.
To fix it:
[Array objectAtIndex:indexPath.section];
Use "section" instead of "row".

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.

NSFetchedResultsControllerDelegate and deleteRowsAtIndexPaths: Section issue

In response of a delete operation on a CELL (not a section) of a tableView connected to a NSFetchedResultsController i obtain this error :
'Invalid update: invalid number of
sections. The number of sections
contained in the table view after the
update (1) must be equal to the number
of sections contained in the table
view before the update (2), plus or
minus the number of sections inserted
or deleted (0 inserted, 0 deleted).'
I understand that the problem is related to Section numbers after and before update. It says that i didn't delete sections, but the after-value is different from the before-value.
Ok this's true! but my section depend on row cells, so if i remove the last row cell of a section, the section disappear.
Here how i define Section and Row numbers:
Sections are created to group row by an attribute "date". Thus, if a row has the attribute "date" 10 April 2010 and a second row has 11 April 2010 i have 2 sections containing 1 row.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.controller sections]count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.controller sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
And here my definition of commitEditingStyle, where i remove cell from table and delete data from DB (object type "Transactions" is a subclass of NSManagedObjectContext, that define my model).
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
Transactions *trans = (Transactions *)[self.controller objectAtIndexPath:indexPath];
//Delete transaction
[self.context deleteObject:trans];
NSError *error = nil;
[self.context save:&error];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationTop];
}
}
I find where's the problem.
The delegate is defined in a controller called from a UITabBar. Every time its view appears, a function is called to Fetch data on NSFEtchedResultController(with performFetch) and setup the delegate for the NSFetchedResultControllerDelegate. This way (i don't know exactly why) every time i try to delete or insert cell/section on table, the delegate seems to be duplicated as many time as i opened the controller via UITabBar, thus Delegate function are repeated more than one time creating problem and errors that i described in the question.
To avoid this problem, now i setup Request and Delegate in init function of ViewController and in no other places. This ensure that it set only one reference to delegate and only one Fetch is performed over NSFetchResultController.

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
}