I have a sorted NSMutableArray which works perfectly and all though when I try to delete an object it crashes the app then when I reload the app it didn't delete the right one.
I known that is due to the fact that this is now a sorted array because before I implemented this feature it worked fine though I haven't got a clue of how to fix it.
Here is the code I use to delete things from the array:
- (void) tableView:(UITableView *)tv commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if ( editingStyle == UITableViewCellEditingStyleDelete ) {
Patient *thisPatient = [patients objectAtIndex:indexPath.row];
[patients removeObjectAtIndex:indexPath.row];
if (patients.count == 0) {
[super setEditing:NO animated:YES];
[self.tableView setEditing:NO animated:YES];
}
[self deletePatientFromDatabase:thisPatient.patientid];
[tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
}
}
It is being stopped at this line:
[tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
Here is the code that I use for sorting the array:
-(void) processForTableView:(NSMutableArray *)items {
for (Patient *thisPatient in items) {
NSString *firstCharacter = [thisPatient.patientName substringToIndex:1];
if (![charIndex containsObject:firstCharacter]) {
[charIndex addObject:firstCharacter];
[charCount setObject:[NSNumber numberWithInt:1] forKey:firstCharacter];
} else {
[charCount setObject:[NSNumber numberWithInt:[[charCount objectForKey:firstCharacter] intValue] + 1] forKey:firstCharacter];
}
}
charIndex = (NSMutableArray *) [charIndex sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
}
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:#"cell"];
NSString *letter = [charIndex objectAtIndex:[indexPath section]];
NSPredicate *search = [NSPredicate predicateWithFormat:#"patientName beginswith[cd] %#", letter];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"patientName" ascending:YES];
NSArray *filteredArray = [[patients filteredArrayUsingPredicate:search] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
if ( nil == cell ) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
NSLog(#"indexPath.row = %d, patients.count = %d", indexPath.row, patients.count);
Patient *thisPatient = [filteredArray objectAtIndex:[indexPath row]];
cell.textLabel.text = [NSString stringWithFormat:#"%# %#", thisPatient.patientName, thisPatient.patientSurname];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.textColor = [UIColor blackColor];
if (self.editing) {
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
}
return cell;
}
I suspect this is quite a common thing that happens when you sort an array though it may not be.
Please say if you want any more code
It's not neccessarily because your array is sorted, but because the array you are populating your table from is not the same as the array that you are removing the object from.
Where are you sorting the patients array? Is the actual patients array being sorted or are you sorting it in your tableView delegates method and not actually sorting patients?
The reason for this is that the index of the object you deleted is not the same as the index that it has in the actual patients array (because one is sorted and one is not). Because of this, it is first deleting the wrong object, then it crashes because the tableView expects one to be deleted (so that it can animate that cell being removed) but the wrong one was deleted.
Related
I have a UITableView populated by a SQLite database. I added Section-based Grouping using the sectionIndexTitlesForTableView delegate method today and now when a Cell is selected, the String for indexPath.row is not the same as the text in the selected Cell.
My Code works like this.
I create an Array that holds the businesses from the SQLite database.
I sort that Array alphabetically.
I create an Array of letters of the Alphabet using only the letters of the Alphabet that businesses in the database begin with.
I use that Array, along with an NSPredicate to provide Grouped Header views which group the businesses by their first letter, alphabetically.
The Selected Row is written to the NSUserDefaults file, and a View Controller is pushed (iPhone), or an Observer is added for that key (iPad).
Unfortunately, since adding the header views, indexPath.row now returns a completely different string to that of the TextLabel of the selected Cell, and so a different Business' information is displayed.
Here are the important blocks of code for the main arrays.
- (void)viewDidLoad
{
// Lots of code...
arrayName = [[NSMutableArray alloc] init];
NameSet = [[NSMutableSet alloc] init];
sortedArray = [arrayName sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
alphabet = [[NSMutableArray alloc] init];
[alphabet addObject:#"{search}"];
for (int i=0; i<[sortedArray count]-1; i++)
{
char alphabetUni = [[sortedArray objectAtIndex:i] characterAtIndex:0];
NSString *uniChar = [NSString stringwithFormat:#"%c", alphabetUni];
if (![alphabet containsObject:uniChar])
{
[alphabet addObject:uniChar];
}
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [alphabet count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger rows = 0;
NSString *alpha = [alphabet objectAtIndex:section];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", alpha];
businesses = [sortedArray filteredArrayUsingPredicate:predicate];
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]){
rows = [self.searchResults count];
}
else {
rows = [businesses count];
}
return rows;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection (NSInteger)section
{
return [alphabet objectAtIndex:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
NSString *alpha = [alphabet objectAtIndex:indexPath.section];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", alpha];
businesses = [sortedArray filteredArrayUsingPredicate:predicate];
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]){
cell.textLabel.text =
[self.searchResults objectAtIndex:indexPath.row];
}
else{
NSString *cellValue = [businesses objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *selected = nil;
if (tableView == self.tableView)
{
selected = [businesses objectAtIndex:indexPath.row];
}
else if (tableView == searchDis.searchResultsTableView)
{
selected = [filteredData objectAtIndex:indexPath.row];
}
[def setObject:selected forKey:#"NameChoiceDetail"];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
[self performSegueWithIdentifier:#"NameDetailPush" sender:self];
}
}
// Please excuse my horribly written code. I've only been working with Objective-C for 4 months, and Programming for about 8 months. Any suggestions/optimisations will be duly noted.
Your table view uses sections but your implementation of tableView:didSelectRowAtIndexPath: doesn't evaluate the section of the index path. So the code is missing something.
Furthermore, I find that your use of the businesses variable (it's probably an instance variable) very strange. It is assigned a value in tableView:cellForRowAtIndexPath: but not in tableView:didSelectRowAtIndexPath: even though it is used there. So the outcome if the latter depends on what table cell was displayed last and as a consequence it depends on scrolling user interaction. That might be the reason why the outcome looks rather random.
i have a UItableView and i have two buttons on each cell. You can add or subtract 1 from the cell's textLabel. I add the cells current value +1 with this:
- (IBAction)addLabelText:(id)sender{
num = [NSString stringWithFormat:#"%d",[cell.textLabel.text intValue] +1];//<--- num is an NSNumber
number = [[NSMutableArray alloc]initWithObjects:num, nil];//<---- number is an NSMutableArray
[myTableView reloadData];
}
and I am trying to subtract the text and store it in an array with this:
- (IBAction)subtractLabelText:(id)sender
{
if ( [[cell.textLabel text] intValue] == 0){
num = [NSString stringWithFormat:#"%d",[num intValue] +0];
[number addObject:num];
[myTableView reloadData];
}
else{
num = [NSString stringWithFormat:#"%d",[num intValue] -1];
[number addObject:num];
[myTableView reloadData];
}
}
and im trying to set the cell.textLabel.text in the cellForRow like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *identifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
}
cell.textLabel.text = [number objectAtIndex:indexPath.row];//<---IM USING THIS LINE TO SET THE NEW TEXTLABEL
cell.textLabel.text = #"1";
return cell;
}
MY PROBLEM
So, the addition works, but the subtraction does not. It doesnt work at all when i press the button on the cell. Thanks in advance!!
At the risk of pointing out the obvious... you seem to have hard-coded the text property to #"1".
cell.textLabel.text = [number objectAtIndex:indexPath.row];//<---IM USING THIS LINE TO SET THE NEW TEXTLABEL
cell.textLabel.text = #"1";
The first line is probably doing what you think... but then you're immediately changing it back to #"1".
EDIT - Based on clarification in comments, here's what I think you want to do. I will modify your own code as posted.
Note that I put my addition into viewDidLoad as an example. You could do this in init, or any number of other places, at whatever point you know how many cells you want to show.
- (void)viewDidLoad {
self.number = [[[NSMutableArray alloc] init] autorelease];
for (int i = 0; i < [HOW MANY CELLS DO YOU WANT?]; i++) {
[self.number addObject:#"1"];
}
[myTableView reloadData];
}
- (IBAction)addLabelText:(id)sender {
// Note that I'm assuming here that your button is a direct child of the cell.
// If not, you'll need to change this.
UITableViewCell *cell = [sender superview];
NSInteger newNumber = [cell.textLabel.text intValue] + 1;
NSString *newNumberString = [NSString stringWithFormat:#"%d", newNumber];
[self.number replaceObjectAtIndex:cell.tag withObject:newNumberString];
[myTableView reloadData];
}
- (IBAction)subtractLabelText:(id)sender {
// Note that I'm assuming here that your button is a direct child of the cell.
// If not, you'll need to change this.
UITableViewCell *cell = [sender superview];
NSInteger newNumber = [cell.textLabel.text intValue] - 1;
newNumber = (newNumber < 0) ? 0 : newNumber;
NSString *newNumberString = [NSString stringWithFormat:#"%d", newNumber];
[self.number replaceObjectAtIndex:cell.tag withObject:newNumberString];
[myTableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
}
cell.tag = indexPath.row; // Important for identifying the cell easily later
cell.textLabel.text = [self.number objectAtIndex:indexPath.row];
return cell;
}
I don't want to get too much more in depth, but I would recommend you take a look at reloading only the cell modified, instead of calling [myTableView reloadData] every time.
I am a noob when it comes to traversing Core Data many-to-many relationships (I have read numerous threads and documentation on this so unless it's and end all, please no links to documentation or threads).
I am making an inventory application and currently have a Core Data model that includes an "Thing", "Category", "Location", and "ThingLocation" (ThingLocation is an entity that holds both a Thing and Location reference but includes the amount of Things on that particular Location. Also a many-to-many relationship) Entities that I would like to populate my UI with. I am proficient in GUI so this is not a question of User Interface but rather how I would gather the information using (probably) NSPredicates.
Ex: If I show a TableView consisting of a Category entity's details then how would I populate it with the Things in that Category Entity.
Ex: If I wanted to display a UILabel showing the total amount of Thing's there were in it. (i.e. add up all of the amounts on each Location).
EDIT: I want to be able to use an NSFetchedResultsController!
I am not exactly sure what your question is asking. So for example you want to iterate over all the categories and all the things in that category you would first do a request for the entities of category without a predicate (this will return all category objects) and iterate over all those with fast enumeration:
//iOS 5 way of doing it
NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntity:#"Category"];
NSArray* arrayOfObjects = [context executeFetchRequest: request withError: nil];
for (Category* cat in arrayOfObjects)
{
//iterate over all the things in that category
for (Thing* thing in cat.things){
{
//do something?
}
}
For your first example of populating a tableview with things in a category,
If you have the category you would get the Things very easily like this:
NSSet* things= category.things;
//you can get it into an array by sorting it somehow or just get it like that.
NSMutableArray* things = [[category.things allObjects] mutableCopy];
You can iterate over this in a very normal fashion or use them as your datasource for your tableview. If you don't have the category you need something to distinguish it in which case you would set up the predicate like this:
NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntity:#"Thing"];
NSPredicate* pred = [NSPredicate predicateWithFormat:#"(relationToCat.DestAtt = %#)",howToDestinguish];
This will return all the Things that are connected to a category that has that attribute.
For your second example, You would set the NSPredicate up to get all the ThingLocations for that specific Thing. Then iterate over them and add up the values at the locations. If you wanted to do this for every category over everything it would just require you to nest some for loop starting with the categories. then for each thing get all the ThingLocations and for each of those add up the values.
I hope that answers your questions. To-Many relations are just sets and can be treated as such. I find that thinking from the bottom up helps me form the predicates. thinking I need all the things in this category so I would set up with the entity of Things and connecting it back to the category in the predicate.
Edit: NSFetchedResultsController example
In your .h file after declaring your super class add NSFetchedResultsControllerDelegate to the delegates implemented.
Create an ivar:
#property (nonatomic, retain) NSFetchedResultsController* fetchedResultsController;
On the implementation side I've seen two different approaches, the first is writing a custom accessor for the property that initializes it there, the other is just to do it in the viewDidLoad in either case the setup is as follows:
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Thing" inManagedObjectContext:context];
NSPredicate* pred=[NSPredicate predicateWithFormat:#"(relToCat.something=%#)",something];
[fetchRequest setPredicate:pred];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20]; //tells it how many objects you want returned at a time.
//this is for displaying the things in some sort of order. If you have a name attribute you'd do something like this
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES] autorelease];
NSArray *sortDescriptors = [[[NSArray alloc] initWithObjects: descriptor, nil] autorelease];
[fetchRequest setSortDescriptors:sortDescriptors];
//this is the set up. If you put a sectionNameKeyPath it will split the results into sections with distinct values for that attribute
NSFetchedResultsController *frc = nil;
frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context] sectionNameKeyPath:#"attribute that splits it into sections" cacheName:nil];
[frc setDelegate:self];
[self setFetchedResultsController:frc];
[frc release];
frc = nil;
//Tells it to start.
[fetchedResultsController performFetch:nil];
Then for the table view delegate methods it is a piece of cake like so:
-(NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
return [[[fetchedResultsController sections] objectAtIndex:section] name];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView {
return [[fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[fetchedResultsController sections] objectAtIndex: section] numberOfObjects];
}
/* If you want the bar on the right with the names of the sections...
-(NSArray*) sectionIndexTitlesForTableView:(UITableView *)tableView{
return [fetchedResultsController sectionIndexTitles];
}*/
-(UITableViewCell* )tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* ident=#"cellident";
UITableViewCell *cell = [self.itemTable dequeueReusableCellWithIdentifier:ident];
if (!cell) {
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:ident] autorelease];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void) configureCell:(UITableViewCell*)cell atIndexPath:(NSIndexPath*)indexPath {
NSManagedObject* item=[fetchedResultsController objectAtIndexPath:indexPath];
//set up your cell somehow
return cell;
}
You also need to add the delegate methods for the fetched results controller. They are all very simple and look something like this:
- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller {
[tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController*) controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
NSIndexSet *set = [NSIndexSet indexSetWithIndex:sectionIndex];
switch(type) {
case NSFetchedResultsChangeInsert: [tableView insertSections:set withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteSections:set withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController*)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath*)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath*)newIndexPath
{
UITableView *tv = tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tv insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tv cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tv insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController*)controller {
[tableView endUpdates];
}
This will update the table if in some other part of the program a thing is added it will automatically show up on the table if the predicate matches.
Problem: When I click the delete button for a given table/section row, i get the following error: "*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (4) must be equal to the number of rows contained in that section before the update (4), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted).'"
From other posts I have read about this symptom, I gather I am suppose to be manually removing an element in my datasource array, but not sure how to access the section's array inside this method:
// COMMIT EDITING STYLE
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"indexPath: %#", indexPath);
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates]; // throws error here
[tableView reloadData];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
I think the complication for this situation arises due to the fact that the plist (FormEntries.plist) I am pulling data from holds user input for all sorts of things all through out my app, thus I am having to call and filter it for every section. This works fine to populate the UITableView and all of it's sections, but because a new filtered array is being created for and inside each section, I'm not sure how to ever access it again in order to remove the element, thus rectifying the above error message. Here is how I am loading the data for each table section:
// CELL FOR ROW AT INDEXPATH
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath (NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSNumber *numScreenId = [[arrayOfModulesScreens objectAtIndex: indexPath.section] objectForKey: #"id"];
NSMutableArray *arrayRecords = [epFrameWork selectPlist: #"FormEntries" filterByKey: #"screen_id" keyValue:numScreenId];
NSString *strTitle = [[arrayRecords objectAtIndex: indexPath.row] objectForKey: #"storage_string"];
cell.textLabel.text = strTitle;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
-- Not sure if this will help diagnose things, but here it is none the less ---
// TITLE FOR HEADER IN SECTION
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [[arrayOfModulesScreens objectAtIndex: section] objectForKey: #"screen_title"];
}
// NUMBER OF SECTIONS IN TABLE VIEW
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [arrayOfModulesScreens count];
}
// NUMBER OF ROWS IN SECTION
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSNumber *numScreenId = [[arrayOfModulesScreens objectAtIndex: section] objectForKey: #"id"];
NSMutableArray *arrayRecords = [epFrameWork selectPlist: #"FormEntries" filterByKey: #"screen_id" keyValue:numScreenId];
int rowCount = [arrayRecords count];
return rowCount;
}
What is the best approach to handle this situation or to resolve the above posted error message?
-- UPDATE --
So here is how I'm trying to identify which plist record to delete, assuming that's what I need to do to resolve the original error:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
int g = indexPath.row;
int count = -1;
UITableViewCell *tvc = [[UITableViewCell alloc] init];
for(id element in tableView.subviews) {
if([element isKindOfClass:[UITableViewCell class]]) {
count +=1;
NSLog(#"g: %d - count: %d", g , count);
if(count == g) {
tvc = element;
NSLog(#"tvc: %# - UID: %# - g: %d - count: %d", tvc, tvc.detailTextLabel.text, g , count);
}
}
}
My logic here was to set a hidden unique identifier on tvc.detailTextLabel.text in the cellForRowAtIndexPath method, which in turn would let me know which record from the plist to filter and delete by calling [array removeObjectAtIndex:uid] where array is my filtered plist array. Only problem now is that tvc in the NSLog always returns the record at index 0, not the row that holds the delete button I click.
NSLog returns: tvc: < UITableViewCell: 0x713e2c0; frame = (0 30; 320 44); text = 'Church A'; autoresize = W; layer = < CALayer: 0x7113e70 > > - UID: -237206321 - g: 3 - count: 3. So why would tvc return the index 0 when it was index 3 I clicked the delete button?
Is this just becoming a clustered mess or is there a cleaner solution? But ya, still stumped.
This error most definitely relates to your mishandling the data that you are trying to load to your table. I found that the easiest and safest way to handle modifying table content is to do something along those lines, with the necessary adjustments (within tableView:commitEditingStyle:)
//REMOVE A CELL FROM A SECTION
[yourTable beginUpdates];
[yourTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationBottom];
[yourTable endUpdates];
[yourTable reloadData];
In addition you need to make sure that your array is properly updated to have the changes reflected in the table.
This is how I was finally able to resolve the issue:
I changed all of this crap:
int g = indexPath.row;
int count = -1;
UITableViewCell *tvc = [[UITableViewCell alloc] init];
for(id element in tableView.subviews) {
if([element isKindOfClass:[UITableViewCell class]]) {
count +=1;
NSLog(#"g: %d - count: %d", g , count);
if(count == g) {
tvc = element;
NSLog(#"tvc: %# - UID: %# - g: %d - count: %d", tvc, tvc.detailTextLabel.text, g , count);
}
}
}
to one simple line:
UITableViewCell *cell = [[self tableView] cellForRowAtIndexPath:indexPath];
This allowed me to identify the cell I was working with. So my final code that worked looks like this:
// COMMIT EDITING STYLE
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
UITableViewCell *cell = [[self tableView] cellForRowAtIndexPath:indexPath];
[epFrameWork deleteRecordFromPlist:#"FormEntries" uid:cell.detailTextLabel.text];
[tableView reloadData];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
-(void) deleteRecordFromPlist:(NSString *)plist uid:(NSString *)uId {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *tmpFileName = [[NSString alloc] initWithFormat:#"%#.plist", plist];
NSString *path = [documentsDirectory stringByAppendingPathComponent:tmpFileName];
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSDictionary *dict = [[NSDictionary alloc] init];
NSString *tmpUid;
for(int i=0; i < [array count]; i++) {
dict = [array objectAtIndex:i];
tmpUid = [dict valueForKey:#"uid"];
if([tmpUid isEqualToString:uId]) {
[array removeObjectAtIndex:i];
[array writeToFile:path atomically:YES];
}
}
}
I am creating Travel guide app using core data. I have 4 entities CITY ,RESTAURANTS ,
HOTEL AND FAMOUS PLACES. City is connected with all other entity because one city may have number of restaurants , hotels and places. City entity has 3 attribute Name ,Image ,Description. I am able to display list restaurants of selected city.In Restaurant Entity I have 4 attribute Name, Description ,Address and phone no..Now I want to show
this attribute detail of selected restaurant(of selected city) in next view.But how can I access restaurant description in next view..
Here is my code.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSMutableArray *restaurants = [[NSMutableArray alloc]initWithArray:[city.cityTorestaurants allObjects]];
NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc]initWithKey:#"Name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:nameDescriptor,nil];
[restaurants sortUsingDescriptors:sortDescriptors];
[hotels sortUsingDescriptors:sortDescriptors];
[self setArrayOfRestaurants:restaurants];
[restaurants release];
[nameDescriptor release];
[sortDescriptors release];
[tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)atableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil ) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier]autorelease];
}
//set up cell
NSLog(#"For Restaurants List");
Restaurants *restaurants = [arrayOfRestaurants objectAtIndex:indexPath.row];
cell.textLabel.text = restaurants.Name;
return cell;
}
cityTorestaurants and restaurantTocity is relationship in core data..
Help Please..
In your RestaurantViewController create a property Restaurant* currentRestaurant
in your didSelectRowAtIndexPath:of the CityViewController set the property with self.restaurantVC.currentRestaurant = [arrayOfRestaurants objectAtIndex:indexPath.row];