Deleting records in UITableViewController throws error
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 tha开发者_如何学Ct 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];
}
}
}
精彩评论