My first question on stackoverflow (<< n00b). I'm working on my first project involving UITableViews and plists, and I'm having an issue.
The property list consists of a dictionary holding 3 sections/categories (each an array) and a number of entries in each of these.
The lists load just fine. The problem doesn't occur until I try making it possible to delete individual entries from the list.
Here's my code:
- (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
//make array of the objects and remove the selected object from that array.
NSMutableArray *delArray = [faves objectForKey:[entries objectAtIndex:indexPath.section]];
[delArray removeObjectAtIndex:indexPath.row];
//set entries to the value of the array, now sans the removed object.
self.entries = delArray;
[delArray release];
//write the updated entries to the plist file.
NSArray *rootPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [rootPath objectAtIndex:0];
NSString *plistFile = [docsPath stringByAppendingPathComponent:@"Data.plist"];
[faves writeToFile:plistFile atomically:YES];
//update the actual tableview. This is where things go awry.
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES]; //ends up trying to load the sections instead of the entries = wrong number of list items returned = crash!
//[tableView reloadData]; //works if I only use this line, but the list updates wrong, showing each entry as a section header.
}
}
The entry IS deleted from the plist correctly, but the table fails to update right and causes the app to crash. I've included the error code below.
2011-09-23 18:40:19.732 MyApp[10314:b303] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1448.89/UITableView.m:974
2011-09-23 18:40:19.734 MyApp[10314:b303] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the table view after the update (5) must be equal to the number of sections contained in the table view before the update (3), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).'
The first number (5) is consistent with how many records are supposed to be left in the affected category, but the second number (3) refers to the number of 开发者_C百科categories. Like I said above, the entry is deleted from the plist - just not the table.
Considering the following two methods:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [entries count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSString *key = [entries objectAtIndex:section];
NSArray *sectionNames = [faves objectForKey:key];
return [sectionNames count];
}
It appears that "entries" is an NSArray of key names (NSString) and that "faves" is an NSDictionary of section content (NSArrays).
The problem is that you are replacing your array of key names (self.entries) with a single section's content array. I've expanded your code to make the logic error a little more obvious:
// query for section key name by section index
NSString * keyName = [entries objectAtIndex:indexPath.section];
// retrieve array of a section's datasource (array of rows)
NSMutableArray * delArray = [faves objectForKey:keyName];
// remove row from a section's datasource
[delArray removeObjectAtIndex:indexPath.row];
// ERROR: assign an array of rows to array used for section names
self.entries = delArray;
Changing your code to the following should remove the assertions generated by the UITableView:
(void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
//make array of the objects and remove the selected object from that array.
NSMutableArray *delArray = [faves objectForKey:[entries objectAtIndex:indexPath.section]];
[delArray removeObjectAtIndex:indexPath.row];
//write the updated entries to the plist file.
NSArray *rootPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [rootPath objectAtIndex:0];
NSString *plistFile = [docsPath stringByAppendingPathComponent:@"Data.plist"];
[faves writeToFile:plistFile atomically:YES];
//update the actual tableview. This is where things go awry.
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES]; //ends up trying to load the sections instead of the entries = wrong number of list items returned = crash!
//[tableView reloadData]; //works if I only use this line, but the list updates wrong, showing each entry as a section header.
}
If tableview delete when sourcing a array from plist file. Documents directory is the best choice.(Refer below) To delete a row and update the plist file again. In UITableViewCellEditingStyleDelete.
in header
@property (nonatomic, retain) NSMutableArray *arForTable;
in CellForRowAtindexpath
cell.textLabel.text=[[ self.arForTable objectAtIndex:indexPath.row]
valueForKey:@"key"];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection
(NSInteger)section
{
return [self.arForTable count];
}
in
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath
*)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"data.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:dataPath])
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:@"data" ofType:@"plist"];
[fileManager copyItemAtPath:bundle toPath:dataPath error:&error];
}
[self.arForTable removeObjectAtIndex:indexPath.row];
[self.arForTable writeToFile:dataPath atomically:YES];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
}
}
精彩评论