使用 CoreData 删除行时 UITableView 崩溃
Posted
技术标签:
【中文标题】使用 CoreData 删除行时 UITableView 崩溃【英文标题】:UITableView crashing when deleting row with CoreData 【发布时间】:2015-08-20 22:50:02 【问题描述】:这是代码:
import UIKit
import CoreData
class ExerciseViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIPickerViewDataSource, UIPickerViewDelegate, NSFetchedResultsControllerDelegate
override func viewDidLoad()
super.viewDidLoad()
VDL()
//sets stepper configs
setsStepper.wraps = false
setsStepper.autorepeat = true
setsStepper.continuous = true
setsStepper.tintColor = UIColor.redColor()
setsStepper.minimumValue = 0
setsStepper.maximumValue = 500
setsStepper.value = 0
//reps stepper configs
repsStepper.wraps = false
repsStepper.autorepeat = true
repsStepper.continuous = true
repsStepper.tintColor = UIColor.orangeColor()
repsStepper.minimumValue = 0
repsStepper.maximumValue = 500
repsStepper.value = 0
exerciseTableView.reloadData()
var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var fetchedResultsController: NSFetchedResultsController?
@IBOutlet var exerciseTableView: UITableView!
@IBOutlet var daysPickerView: UIPickerView!
@IBOutlet var exerciseName: UITextField!
@IBOutlet var setsStepper: UIStepper!
@IBOutlet var repsStepper: UIStepper!
@IBOutlet var setsNumber: UILabel!
@IBOutlet var repsNumber: UILabel!
var daysArray = [TrainingDay]()
var detailsArray = [TrainingDetails]()
func VDL ()
let fetchRequest = NSFetchRequest(entityName: "TrainingDay")
let sort = NSSortDescriptor(key: "dayIndex", ascending: true)
fetchRequest.sortDescriptors = [sort]
daysArray = (moc!.executeFetchRequest(fetchRequest, error: nil) as? [TrainingDay])!
if daysArray.count == 0 // nothing there
let dayEntity = NSEntityDescription.entityForName("TrainingDay", inManagedObjectContext: moc!)
let days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
for (index, name) in enumerate(days)
let newDay = TrainingDay(entity: dayEntity!, insertIntoManagedObjectContext: moc)
newDay.day = name
newDay.dayIndex = index
daysArray.append(newDay)
println("NAME: \(newDay.day) INDEX: \(newDay.dayIndex)")
var error: NSError?
moc!.save(&error)
func appendTrainingDetailsToArray ()
let row = daysPickerView.selectedRowInComponent(0)
let currentDay = daysArray[row]
let detailsEntity = NSEntityDescription.entityForName("TrainingDetails", inManagedObjectContext: moc!)
let trainingdetails = TrainingDetails(entity: detailsEntity!, insertIntoManagedObjectContext: moc)
trainingdetails.exerciseName = exerciseName.text
trainingdetails.repsNumber = repsNumber.text!
trainingdetails.setsNumber = setsNumber.text!
trainingdetails.trainingDay = currentDay
var error: NSError?
moc?.save(&error)
if let err = error
var status = err.localizedFailureReason
println("\(status)")
else
println("CURRENT SETTING: \(trainingdetails.trainingDay)")
func fetchTrainingDetails() -> NSFetchRequest
let fetchRequest = NSFetchRequest(entityName: "TrainingDetails")
fetchRequest.predicate = nil
let sortDescriptor = NSSortDescriptor(key: "trainingDay", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.fetchBatchSize = 20
return fetchRequest
@IBAction func doneButton(sender: AnyObject)
appendTrainingDetailsToArray()
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchTrainingDetails(), managedObjectContext: moc!, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController?.delegate = self
fetchedResultsController?.performFetch(nil)
exerciseTableView.reloadData()
@IBAction func setsStepperAction(sender: UIStepper)
println("\(Int(sender.value))")
setsNumber.text = Int(sender.value).description
@IBAction func repsStepper(sender: UIStepper)
println("\(Int(sender.value))")
repsNumber.text = Int(sender.value).description
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return fetchedResultsController?.sections?[section].numberOfObjects ?? 0
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCellWithIdentifier("exerciseCell", forIndexPath: indexPath) as! UITableViewCell
let details = fetchedResultsController!.objectAtIndexPath(indexPath) as! TrainingDetails
cell.textLabel!.text = "\(details.exerciseName)"
cell.detailTextLabel!.text = "Sets: #\(details.setsNumber) Reps: #\(details.repsNumber)"
return cell
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool
return true
func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle
return UITableViewCellEditingStyle.Delete
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
println("section and row \(indexPath.section) \(indexPath.row) ")
if (editingStyle == UITableViewCellEditingStyle.Delete)
let detailsForRow : NSManagedObject = fetchedResultsController!.objectAtIndexPath(indexPath) as! TrainingDetails
moc?.deleteObject(detailsForRow)
moc?.save(nil)
exerciseTableView.beginUpdates()
exerciseTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade)
exerciseTableView.endUpdates()
//PICKER VIEW DELEGATE AND DATASOURCE METHODS
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int
return 1
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
return daysArray.count
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String!
let trainingDay = daysArray[row]
return trainingDay.day
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
let currentDay = daysArray[row]
let fetchRequest = NSFetchRequest(entityName: "TrainingDetails")
let predicate = NSPredicate(format: "trainingDay = %@", currentDay)
fetchRequest.predicate = predicate
let sort = NSSortDescriptor(key: "exerciseName", ascending: true)
fetchRequest.sortDescriptors = [sort]
detailsArray = (moc!.executeFetchRequest(fetchRequest, error: nil) as? [TrainingDetails])!
exerciseTableView.reloadData()
// MARK: NSFetchedResultsControllerDelegate
func controllerWillChangeContent(controller: NSFetchedResultsController)
self.exerciseTableView.beginUpdates()
func controller(controller: NSFetchedResultsController,
didChangeObject anObject: AnyObject,
atIndexPath indexPath: NSIndexPath?,
forChangeType type: NSFetchedResultsChangeType,
newIndexPath: NSIndexPath?)
switch type
case NSFetchedResultsChangeType.Insert:
// Note that for Insert, we insert a row at the __newIndexPath__
if let insertIndexPath = newIndexPath
self.exerciseTableView.insertRowsAtIndexPaths([insertIndexPath], withRowAnimation: UITableViewRowAnimation.Fade)
case NSFetchedResultsChangeType.Delete:
// Note that for Delete, we delete the row at __indexPath__
if let deleteIndexPath = indexPath
self.exerciseTableView.deleteRowsAtIndexPaths([deleteIndexPath], withRowAnimation: UITableViewRowAnimation.Fade)
case NSFetchedResultsChangeType.Update:
// Note that for Update, we update the row at __indexPath__
if let updateIndexPath = indexPath
let cell = self.exerciseTableView.cellForRowAtIndexPath(updateIndexPath)
let details = self.fetchedResultsController!.objectAtIndexPath(updateIndexPath) as? TrainingDetails
cell!.textLabel!.text = "\(details!.exerciseName)"
cell!.detailTextLabel!.text = "Sets: #\(details!.setsNumber) Reps: #\(details!.repsNumber)"
case NSFetchedResultsChangeType.Move:
// Note that for Move, we delete the row at __indexPath__
if let deleteIndexPath = indexPath
self.exerciseTableView.deleteRowsAtIndexPaths([deleteIndexPath], withRowAnimation: UITableViewRowAnimation.Fade)
// Note that for Move, we insert a row at the __newIndexPath__
if let insertIndexPath = newIndexPath
self.exerciseTableView.insertRowsAtIndexPaths([insertIndexPath], withRowAnimation: UITableViewRowAnimation.Fade)
func controller(controller: NSFetchedResultsController,
didChangeSection sectionInfo: NSFetchedResultsSectionInfo,
atIndex sectionIndex: Int,
forChangeType type: NSFetchedResultsChangeType)
switch type
case .Insert:
let sectionIndexSet = NSIndexSet(index: sectionIndex)
self.exerciseTableView.insertSections(sectionIndexSet, withRowAnimation: UITableViewRowAnimation.Fade)
case .Delete:
let sectionIndexSet = NSIndexSet(index: sectionIndex)
self.exerciseTableView.deleteSections(sectionIndexSet, withRowAnimation: UITableViewRowAnimation.Fade)
default:
""
func controllerDidChangeContent(controller: NSFetchedResultsController)
exerciseTableView.endUpdates()
每当我尝试从 tableView 中删除一行时,应用程序就会崩溃。我读到了一些关于numberOfRowInSection
在commingEditingStyle
内部被调用的内容。有人知道吗?
我尝试了许多不同的方法来擦除它,但它不会消失。
更新
好吧,我是这样设计的:
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
println("section and row \(indexPath.section) \(indexPath.row) ")
if self.fetchedResultsController == nil
println("error when trying to delete object from managed object")
else if (editingStyle == UITableViewCellEditingStyle.Delete)
moc?.deleteObject(detailsArray[indexPath.row] as NSManagedObject)
detailsArray.removeAtIndex(indexPath.row)
var error: NSError?
moc?.save(&error)
现在,当我尝试删除某些内容时,它运行正常,但前提是它是表格视图中的第一项。如果我尝试删除表格视图中间的某些内容,应用程序会因 fatal error: Array index out of range
而崩溃另外,我如何在 Swift 中做到这一点?
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
if (self.fetchedResultsController == nil)
else
// Do stuff
if (editingStyle == UITableViewCellEditingStyleDelete)
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
NSError *error = nil;
if (![context save:&error])
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
//abort();
谢谢!!
【问题讨论】:
崩溃是数组索引越界?显示异常消息。你在哪里设置节数? 不要尝试从 tableView 中删除任何内容 - 使用 NSFetchedResultsController 和 NSFetchedResultsControllerDelegate 方法在 UITableView 中呈现代码数据记录。你所做的只是从 Core Data 中删除对象,UITableView 会神奇地更新自己,类似地,当你创建一个新的 Core Data 对象时,它也会神奇地出现在 UITableView 中。 developer.apple.com/library/ios/documentation/CoreData/… 【参考方案1】:您的 Objective C 代码转换为 Swift
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
if self.fetchedResultsController == nil
else
if editingStyle == UITableViewCellEditingStyleDelete
var context: NSManagedObjectContext = self.fetchedResultsController.managedObjectContext()
context.deleteObject(self.fetchedResultsController.objectAtIndexPath(indexPath))
var error: NSError? = nil
if !context.save(&error)
NSLog("Unresolved error %@, %@", error, error.userInfo())
这里是 Link 将 Objective C 代码转换为 Swift
希望这会有所帮助。
【讨论】:
【参考方案2】:除了我上面的评论之外,还有一个使用 NSFetchedResultsController 的 UITableView 示例。
下面的代码包括添加和删除 Core Data 对象的代码,这样 UITableView 将自动更新自身以显示/删除相关对象。
您可以在这里http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/下载完整的示例应用程序
//
// MasterViewController.m
// CoreDataLibraryApp
//
//
#import "CompanyViewController.h"
#import "OSCDStackManager.h"
#import "CompanyDetailViewController.h"
@interface CompanyViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
@end
@implementation CompanyViewController
- (void)awakeFromNib
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
self.clearsSelectionOnViewWillAppear = NO;
self.preferredContentSize = CGSizeMake(320.0, 600.0);
[super awakeFromNib];
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//self.navigationItem.leftBarButtonItem = self.editButtonItem;
self.navigationItem.leftItemsSupplementBackButton = YES;
[self.navigationItem setHidesBackButton:NO];
// Uncomment the following line to preserve selection between presentations.
self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
//self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(storeChanged) name:OSStoreChangeNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshUI) name:OSDataUpdatedNotification
object:nil];
UIRefreshControl *refresh = [[UIRefreshControl alloc] init];
refresh.attributedTitle = [[NSAttributedString alloc] initWithString:@"Pull to Load"];
[refresh addTarget:self action:@selector(refresh)
forControlEvents:UIControlEventValueChanged];
self.managedObjectContext = [[OSCDStackManager sharedManager] managedObjectContext];
FLOG(@"managedObjectContext is %@",self.managedObjectContext);
[self fetchedResultsController];
self.detailViewController.detailItem = nil;
- (void)stopRefresh
[self.refreshControl endRefreshing];
- (void)refresh
FLOG(@"refresh called");
[[OSCDStackManager sharedManager] loadDataInBackground];
[self performSelector:@selector(stopRefresh) withObject:nil afterDelay:0.5];
-(void)storeChanged
FLOG(@"storeChanged called");
_fetchedResultsController = nil;
self.managedObjectContext = [[OSCDStackManager sharedManager] managedObjectContext];
FLOG(@"managedObjectContext is %@",self.managedObjectContext);
[self fetchedResultsController];
self.detailViewController.detailItem = nil;
[[self tableView] reloadData];
-(void)refreshUI
FLOG(@"refreshUI called");
/*
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error])
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
//abort();
*/
[[self tableView] reloadData];
- (void)didReceiveMemoryWarning
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
- (void)insertNewObject:(id)sender
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
NSString *str = ([[OSCDStackManager sharedManager] isCloudEnabled] ? @"New Company(c)" : @"New Company");
// If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
[newManagedObject setValue:str forKey:@"name"];
// Save the context.
NSError *error = nil;
if (![context save:&error])
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
//abort();
#pragma mark - Table View
// if fetchedResultsController == nil it means the database has not been initialised yet
// and so we just return a single row saying "Loading, please wait..."
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
// return 1 section of fetechedResultsController == nil
if (self.fetchedResultsController == nil)
return 1;
else
return [[self.fetchedResultsController sections] count];
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
if (self.fetchedResultsController == nil)
return 1;
else
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo numberOfObjects];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
[self configureCell:cell atIndexPath:indexPath];
return cell;
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
if (self.fetchedResultsController == nil)
return NO;
else
// Return NO if you do not want the specified item to be editable.
return YES;
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
if (self.fetchedResultsController == nil)
else
// Do stuff
if (editingStyle == UITableViewCellEditingStyleDelete)
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
NSError *error = nil;
if (![context save:&error])
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
//abort();
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
// The table view should not be re-orderable.
return NO;
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
// Don't allow user to select a row if Core Data stack is not set up yet.
if (self.fetchedResultsController == nil)
NSLog(@" fetchedResultsController == nil");
return nil;
// By default, allow row to be selected
return indexPath;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
FLOG(@" called");
if (self.fetchedResultsController == nil)
FLOG(@" fetchedResultsController == nil");
else
FLOG(@" fetchedResultsController != nil");
CompanyViewController *newTableViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"CompanyMenuTableViewController"];
newTableViewController.detailItem = [[self fetchedResultsController] objectAtIndexPath:indexPath];
[self.navigationController pushViewController:newTableViewController animated:YES];
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
FLOG(@" called");
if (self.fetchedResultsController == nil)
FLOG(@" fetchedResultsController == nil");
else
FLOG(@" fetchedResultsController != nil");
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
// Create and configure a new detail view controller appropriate for the selection.
self.detailViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"CompanyDetailViewController"];
NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
self.detailViewController.detailItem = object;
//self.detailViewController.title = [object valueForKey:@"name"];
[self.navigationController pushViewController:self.detailViewController animated:YES];
else
// We want to highlight the selected row
[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
FLOG(@" Device is iPad");
DetailViewManager *detailViewManager = (DetailViewManager*)self.splitViewController.delegate;
FLOG(@" detailViewManager is %@", detailViewManager);
// Create and configure a new detail view controller appropriate for the selection.
self.detailViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"CompanyDetailViewController"];
NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
self.detailViewController.detailItem = object;
//self.detailViewController.title = [object valueForKey:@"name"];
detailViewManager.detailViewController = self.detailViewController;
//
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
if ([[segue identifier] isEqualToString:@"showCompanyDetail"])
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
[[segue destinationViewController] setDetailItem:object];
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
if (_fetchedResultsController != nil)
return _fetchedResultsController;
if (self.managedObjectContext == nil) return nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Company" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = @[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error])
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
return _fetchedResultsController;
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
NSLog(@"controllerWillChangeContent called");
[self.tableView beginUpdates];
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
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
UITableView *tableView = self.tableView;
switch(type)
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
NSLog(@"controllerDidChangeContent called");
[self.tableView endUpdates];
/*
// Implementing the above methods to update the table view in response to individual changes may have performance implications if a large number of changes are made simultaneously. If this proves to be an issue, you can instead just implement controllerDidChangeContent: which notifies the delegate that all section and object changes have been processed.
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
// In the simplest, most efficient, case, reload the table view.
[self.tableView reloadData];
*/
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
if (self.fetchedResultsController == nil)
cell.textLabel.text = @"Loading please wait...";
cell.accessoryType = UITableViewCellAccessoryNone;
UIActivityIndicatorView *progressView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
cell.accessoryView = progressView;
[progressView startAnimating];
else
cell.accessoryView = nil;
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[object valueForKey:@"name"] description];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
@end
【讨论】:
以上是关于使用 CoreData 删除行时 UITableView 崩溃的主要内容,如果未能解决你的问题,请参考以下文章
UIViewController里面的CoreData TableView
在使用 CoreData 的 TableView 中将行移至底部