在我的iOS应用程序中,我有一个包含部分的表视图,所有部分都是从核心数据中填充的。 如果删除部分的最后一行,则应用程序崩溃。 我无法找到代码中的问题,请您帮助我。先感谢您。到目前为止,这是我的代码:
#import "PersonsTVC.h"
#import "Person.h"
@implementation PersonsTVC
@synthesize fetchedResultsController = __fetchedResultsController;
@synthesize managedObjectContext = __managedObjectContext;
@synthesize selectedPerson;
@synthesize searchResults,titulosseccion;
- (void)setupFetchedResultsController
{
// 1 - Decide what Entity you want
NSString *entityName = @"Person"; // Put your entity name here
NSLog(@"Setting up a Fetched Results Controller for the Entity named %@", entityName);
// 2 - Request that Entity
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
// 3 - Filter it if you want
//request.predicate = [NSPredicate predicateWithFormat:@"Person.name = Blah"];
// 4 - Sort it if you want
// First sort descriptor (required for grouping into sections):
NSSortDescriptor *sortByDate = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO];
// Second sort descriptor (for the items within each section):
NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:@"firstname" ascending:YES];
[request setSortDescriptors:@[sortByDate, sortByName]];
//
// request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"firstname"
//ascending:YES
//selector:@selector(localizedCaseInsensitiveCompare:)]];
// 5 - Fetch it
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:@"sectionIdentifier"
cacheName:nil];
[self performFetch];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> theSection = [[self.fetchedResultsController sections] objectAtIndex:section];
NSString *sectionName = [theSection name];
if ([sectionName isEqualToString:@"0"]) {
return @"Today";
} else if ([sectionName isEqualToString:@"1"]) {
return @"Tomorrow";
}
else if ([sectionName isEqualToString:@"2"]) {
return @"Upcoming";
}
return @"Other";
}
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [[self.fetchedResultsController sections] count];
}
- (void) viewDidLoad
{
self.searchResults = [NSMutableArray arrayWithCapacity:[[self.fetchedResultsController fetchedObjects] count]];
[self.tableView reloadData];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self setupFetchedResultsController];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Perform segue to detail when a SEARCH table cell is touched
if(tableView == self.searchDisplayController.searchResultsTableView)
{
[self performSegueWithIdentifier:@"Person Detail Segue" sender:tableView];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Persons Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
// Configure the cell...
Person *person = nil;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
NSLog(@"Configuring cell to show search results");
person = [self.searchResults objectAtIndex:indexPath.row];
}
else
{
NSLog(@"Configuring cell to show normal data");
person = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
NSString *fullname = [NSString stringWithFormat:@"%@ %@", person.firstname, person.surname];
cell.textLabel.text = person.firstname;
if ([person.inRole.color isEqual :@"Yellow"])
{
cell.imageView.image = [UIImage imageNamed:@"Yellow"];
}
if ([person.inRole.color isEqual :@"Black"])
{
cell.imageView.image = [UIImage imageNamed:@"Black"];
}
if ([person.inRole.color isEqual :@"Grey"])
{
cell.imageView.image = [UIImage imageNamed:@"Grey"];
}
if ([person.inRole.color isEqual :@"Red"])
{
cell.imageView.image = [UIImage imageNamed:@"Red"];
}
if ([person.inRole.color isEqual :@"Blue"])
{
cell.imageView.image = [UIImage imageNamed:@"Blue"];
}
if ([person.inRole.color isEqual :@"Dark Green"])
{
cell.imageView.image = [UIImage imageNamed:@"DarkGreen"];
}
if ([person.inRole.color isEqual :@"Light Green"])
{
cell.imageView.image = [UIImage imageNamed:@"LightGreen"];
}
if ([person.inRole.color isEqual :@"Light Blue"])
{
cell.imageView.image = [UIImage imageNamed:@"LightBlue"];
}
if ([person.inRole.color isEqual :@"Brown"])
{
cell.imageView.image = [UIImage imageNamed:@"Brown"];
}
if ([person.inRole.color isEqual :@"Dark Orange"])
{
cell.imageView.image = [UIImage imageNamed:@"DarkOrange"];
}
NSDate *fechasinformat = person.date;
NSString *fecha0 = [NSString stringWithFormat:@"%@", fechasinformat];
cell.detailTextLabel.text = fecha0;
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView)
{
return [self.searchResults count];
}
else
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.tableView beginUpdates]; // Avoid NSInternalInconsistencyException
// Delete the person object that was swiped
Person *personToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSLog(@"Deleting (%@)", personToDelete.firstname);
[self.managedObjectContext deleteObject:personToDelete];
[self.managedObjectContext save:nil];
// Delete the (now empty) row on the table
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self performFetch];
[self.tableView endUpdates];
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"Add Person Segue"])
{
NSLog(@"Setting PersonsTVC as a delegate of PersonDetailTVC");
PersonDetailTVC *personDetailTVC = segue.destinationViewController;
personDetailTVC.delegate = self;
NSLog(@"Creating a new person and passing it to PersonDetailTVC");
Person *newPerson = [NSEntityDescription insertNewObjectForEntityForName:@"Person"
inManagedObjectContext:self.managedObjectContext];
personDetailTVC.person = newPerson;
}
else if ([segue.identifier isEqualToString:@"Person Detail Segue"])
{
NSLog(@"Setting PersonsTVC as a delegate of PersonDetailTVC");
PersonDetailTVC *personDetailTVC = segue.destinationViewController;
personDetailTVC.delegate = self;
// Store selected Person in selectedPerson property
if(sender == self.searchDisplayController.searchResultsTableView)
{
NSIndexPath *indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
self.selectedPerson = [self.searchResults objectAtIndex:[indexPath row]];
}
else
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
self.selectedPerson = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
NSLog(@"Passing selected person (%@) to PersonDetailTVC", self.selectedPerson.firstname);
personDetailTVC.person = self.selectedPerson;
}
else
{
NSLog(@"Unidentified Segue Attempted!");
}
}
- (void)theSaveButtonOnThePersonDetailTVCWasTapped:(PersonDetailTVC *)controller
{
// do something here like refreshing the table or whatever
// close the delegated view
[controller.navigationController popViewControllerAnimated:YES];
}
#pragma mark -
#pragma mark Content Filtering
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
self.searchResults = [[self.fetchedResultsController fetchedObjects] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
Person* person = evaluatedObject;
NSString* firstName = person.firstname;
//searchText having length < 3 should not be considered
if (!!searchText && [searchText length] < 3) {
return YES;
}
if ([scope isEqualToString:@"All"] || [firstName isEqualToString:scope]) {
return ([firstName rangeOfString:searchText].location != NSNotFound);
}
return NO; //if nothing matches
}]];
}
#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:@"All"];
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
[self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:@"All"];
return YES;
}
@end
答案 0 :(得分:2)
错误清楚地表明您删除了1个部分,删除前有1个,删除后有1个。这就是问题。可能你的
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
删除最后一个对象后,返回的部分数量不正确。
删除最后一个对象后,[[self.fetchedResultsController sections] count]
不为0,numberOfSectionsInTableView:(UITableView *)tableView
调用self.searchDisplayController.searchResultsTableView
,您不会检查该内容。
编辑:要解决此问题,请添加以下代码:
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
if(tableView==self.searchDisplayController.searchResultsTableView)
NSLog("search table asking");
else
NSLog("main table asking");
NSLog("fetched sections: %d", [[self.fetchedResultsController sections] count]);
return [[self.fetchedResultsController sections] count];
}
并在发生崩溃时发布控制台输出。
答案 1 :(得分:1)
要实现NSFetchedResultsController委托方法,请将以下代码添加到viewController中(我删除了一些代码以便检查它是否已编译),并在创建控制器时使用_fetchedResultsController.delegate = self;
之类的内容将viewController设置为委托(在setupFetchedResultsController)。
任何时候添加,修改或删除fetchedResultsController结果集中的managedObject都会调用这些方法。您可以看到它们包含对UITableView的必要调用以更新显示。这些与使用fetchedResultsController作为UITableView的数据源一起使用 - 这样就不会出现任何不一致错误。
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
NSLog(@"controllerWillChangeContent: called");
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{ NSLog(@"didChangeSection: called");
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
NSLog(@"controller:didChangeObject: called");
NSLog(@" object is %@", [anObject valueForKey:@"displayName"]);
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
{ NSLog(@"NSFetchedResultsChangeInsert: called");
[tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}
break;
case NSFetchedResultsChangeDelete:
{ NSLog(@"NSFetchedResultsChangeDelete: called");
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
break;
case NSFetchedResultsChangeUpdate:
{ NSLog(@"NSFetchedResultsChangeUpdate: called");
[self configureCell:tableView cell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
}
break;
case NSFetchedResultsChangeMove:
{ NSLog(@"NSFetchedResultsChangeMove: called");
self.changedObjectIndex = newIndexPath;
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}
break;
}
}
// NOTE: This is overridden by subclasses to modify the default behaviour
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
NSLog(@“controllerDidChangeContent: called");
[self.tableView endUpdates];
}
- (void)configureCell:(UITableView *)tableView cell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *object;
object = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[object valueForKey:@"displayName"] description];
bool found =[[object valueForKey:@"found"] boolValue];
if (found) {
cell.textLabel.textColor = [UIColor greenColor];
}
}
答案 2 :(得分:0)
一个正确的解决方案是让你的PersonsTVC
成为获取结果控制器的代表
如果您打开一个使用CoreData的空项目,您将更好地理解并可能找到您尝试完成的解决方案。
以最简单的形式,您的代码应为:
- (void)setupFetchedResultsController
{
//Setup your fetch request ...
NSFetchedResultsController* controller =
[[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:@"sectionIdentifier"
cacheName:nil];
//setup the delegation and retain the FRC
controller.delegate = self;
self.fetchedResultsController = controller;
NSError* error = nil;
if (![controller performFetch:&error]) {
NSLog(@"error performing fetch: %@",error);
abort();
}
//Not sure what this does, seems redundant
//[self performFetch];
}
现在您必须通过您创建的FRC访问您的托管对象(不确定为什么需要self.searchResults
)。
正如我所说,以最简单的形式,执行:
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
// In the simplest, most efficient, case, reload the table view.
[self.tableView reloadData];
}
注意:您的删除代码应为:
这也是您的应用程序崩溃的原因,因为您在FRC能够报告上下文中的更改之前更新了表。
- (void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
NSError *error = nil;
if (![context save:&error]) {
NSLog(@"Unresolved error %@", error);
abort();
}
}
}