2016-01-14 4 views
0

Привет, ребята Я использую NSFetchedResultController, но у меня возникают проблемы, когда я удаляю запись (даже с первой записью).Проблема с удалением строки с использованием NSFetchedResultController в swift 2.1

Вот код моего View Controller:

import UIKit 
import CoreData 

class FoldersListViewController: UITableViewController, NSFetchedResultsControllerDelegate { 

@IBOutlet var myFoldersTableView: UITableView! 

// public property that represent the current selected row in a tableview; this will be use in the shouldPerformSegueWithIdentifier 
// function because the method doesn't have a parameter for the index path of the selected row. 
var selectedRowIndex: NSIndexPath? = nil 

//Public property that will be controlling all the manipulation of CoreData. 
var fetchedResultController: NSFetchedResultsController! 


override func viewDidLoad() { 
    super.viewDidLoad() 

    let config = Settings() 
    config.ReadConfiguration() 


    // Uncomment the following line to preserve selection between presentations 
    // self.clearsSelectionOnViewWillAppear = false 

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller. 
    //self.navigationItem.rightBarButtonItem = self.editButtonItem() 
    self.loadFolders() 


} 

override func didReceiveMemoryWarning() { 
    super.didReceiveMemoryWarning() 
    // Dispose of any resources that can be recreated. 
} 

override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 
    // #warning Potentially incomplete method implementation. 
    // Return the number of sections. 
    return fetchedResultController.sections!.count 
} 

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
    // #warning Incomplete method implementation. 
    // Return the number of rows in the section. 
    let sectionInfo = fetchedResultController.sections![section] 

    return sectionInfo.numberOfObjects 
} 

func configureCell(cell: cellFolder,indexPath: NSIndexPath) 
{ 
    let folder = fetchedResultController.objectAtIndexPath(indexPath) as! Folder 

    if(folder.picture.length > 0){ 
     let newSize:CGSize = CGSize(width: 64,height: 64) 
     let rect = CGRectMake(0,0, newSize.width, newSize.height) 
     UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0) 
     let photo = UIImage(data: folder.picture) 

     UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) 

     photo!.drawInRect(rect) 
     let newImage = UIGraphicsGetImageFromCurrentImageContext() 
     UIGraphicsEndImageContext() 

     cell.imageView?.contentMode = UIViewContentMode.ScaleAspectFill 
     cell.imageView?.layer.masksToBounds = true 
     cell.imageView?.layer.cornerRadius = 15.0 
     cell.imageView?.clipsToBounds = true 
     cell.imageView?.image = newImage 

    } 

    cell.labelFolderName.text = folder.name 


} 
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    let cell: cellFolder = tableView.dequeueReusableCellWithIdentifier("cellFolder", forIndexPath: indexPath) as! cellFolder 

    self.configureCell(cell, indexPath: indexPath) 

    return cell 
} 


// Override to support conditional editing of the table view. 
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { 
    // Return NO if you do not want the specified item to be editable. 
    if (tableView.editing){ 
     return false 
    } 
    else 
    { 
     return true 
    } 
} 

func loadFolders() { 
    let request = NSFetchRequest(entityName: "Folder") 
    let handler = HACoreDataHandler() 
    let sortDescriptor = NSSortDescriptor(key: "name",ascending: true) 

    request.sortDescriptors = [sortDescriptor] 

    fetchedResultController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: handler.context!, sectionNameKeyPath: nil, cacheName: nil) 
    fetchedResultController.delegate = self 

    do 
    { 
     try fetchedResultController.performFetch() 

    } catch let error as NSError { 
     let alert = UIAlertController(title: "Error", message: error.description, preferredStyle: UIAlertControllerStyle.Alert) 
     let dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction) -> 
      Void in 
     } 

     alert.addAction(dismiss) 
     presentViewController(alert, animated: true, completion: nil) 
    } 
} 

func controllerWillChangeContent(controller: NSFetchedResultsController) { 
    tableView.beginUpdates() 
} 


func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) 
{ 
    let indexSet = NSIndexSet(index:sectionIndex) 
    switch type { 
    case .Insert: 
     myFoldersTableView.insertSections(indexSet, withRowAnimation: .Automatic) 
    case .Delete: 
     myFoldersTableView.deleteSections(indexSet, withRowAnimation: .Automatic) 
    default: 
     break 
    } 
} 

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) 
{ 
    switch type{ 
     case .Insert: 
      myFoldersTableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Automatic) 
     case .Delete: 
      myFoldersTableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic) 
     case .Update: 
      myFoldersTableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic) 
     case .Move: 
      if (indexPath != newIndexPath){ 
       myFoldersTableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic) 
       myFoldersTableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Automatic) 
      } 
    } 
} 


func controllerDidChangeContent(controller: NSFetchedResultsController) { 
    myFoldersTableView.endUpdates() 
} 
override func viewWillAppear(animated: Bool) { 
    self.loadFolders() 
    myFoldersTableView.reloadData() 
} 

// Override to support editing the table view. 
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { 
    if editingStyle == .Delete { 
     // Delete the row from the data source 
     let item = self.fetchedResultController.objectAtIndexPath(indexPath) as! Folder 
     let handler = HACoreDataHandler() 

     handler.context?.deleteObject(item) 

     var error: NSError? 

     do { 
      try handler.context?.save() 
     } catch let error1 as NSError { 
      error = error1 
     } 

     if(error != nil){ 

      let alert = UIAlertController(title: "Error", message: error?.description, preferredStyle: UIAlertControllerStyle.Alert) 

      let dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction) -> 
       Void in 
      } 

      alert.addAction(dismiss) 

      presentViewController(alert, animated: true, completion: nil) 
     } 

     self.myFoldersTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) 
    } 
} 


override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? { 
    let delete = UITableViewRowAction(style: .Default, title: "Delete") { (action:UITableViewRowAction, indexPath:NSIndexPath) -> Void in 

     self.selectedRowIndex = indexPath 

     let folder = self.fetchedResultController.objectAtIndexPath(indexPath) as! Folder 

     let handler = HACoreDataHandler() 

     handler.context?.deleteObject(folder) 

     do { 
      try handler.context?.save() 

     } catch let error as NSError { 

      let alert = UIAlertController(title: "Error", message: error.description, preferredStyle: UIAlertControllerStyle.Alert) 

      let dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction) -> 
       Void in 
      } 

      alert.addAction(dismiss) 

      self.presentViewController(alert, animated: true, completion: nil) 
     } 

     self.myFoldersTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade) 

    } 

    delete.backgroundColor = UIColor.redColor() 

    let details = UITableViewRowAction(style: .Normal, title: "Edit") { (action: UITableViewRowAction, indexPath: NSIndexPath) -> Void in 
     let tmpFolder = self.fetchedResultController.objectAtIndexPath(indexPath) as! Folder 

     let destination = self.storyboard?.instantiateViewControllerWithIdentifier("editFolder") as! EditFolderViewController 

     destination.folder = tmpFolder 

     let navigationController = self.parentViewController as! UINavigationController 

     var response: Bool = true 

     if(tmpFolder.isprotected == true){ 
      let alert = UIAlertController(title: "Password Validation", message: "Please enter the folder password", preferredStyle: .Alert) 

      //2. Add the text field. You can configure it however you need. 
      alert.addTextFieldWithConfigurationHandler({ (textField) -> Void in 
       textField.text = "" 
      }) 

      alert.addAction(UIAlertAction(title: "Login", style: .Default, handler: { (action) -> Void in 
       let textField = alert.textFields![0] 
       if (tmpFolder.password == textField.text){ 
        response = true 
       } else { 
        let wrongPasswordAlert = UIAlertController(title: "Error", message: "Invalid password.", preferredStyle: UIAlertControllerStyle.Alert) 

        let dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction) -> 
         Void in 
        } 

        wrongPasswordAlert.addAction(dismiss) 

        self.presentViewController(wrongPasswordAlert, animated: true, completion: nil) 
        response = false 

       } // else if (folder.password == textField.text) 

      })) 

      // 4. Present the alert. 
      self.presentViewController(alert, animated: true, completion: nil) 

     } 

     // 4. Present the alert. 
     if(response){ 
      navigationController.pushViewController(destination, animated: true) 
     } 
     //navigationController.presentViewController(destination, animated: true, completion: nil) 
     //self.showDetailViewController(destination, sender: self) 

    } 

    details.backgroundColor = UIColor.grayColor() 
    return [delete,details] 

} 


// Override to support rearranging the table view. 
//override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) { 
// 
//} 

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 
    myFoldersTableView.reloadData() 
} 

// Override to support conditional rearranging of the table view. 
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool { 
    // Return NO if you do not want the item to be re-orderable. 
    return true 
} 

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 
    if(segue.identifier=="listOcassions"){ 
     let ocassions = segue.destinationViewController as! DatesViewController 
     let folder = fetchedResultController.objectAtIndexPath(self.selectedRowIndex!) as! Folder 
     ocassions.folder = folder 
    } 
} 

override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool { 
    var response: Bool = true 

     if identifier == "listOcassions" { 
      self.selectedRowIndex = myFoldersTableView.indexPathForSelectedRow 
      let folder = fetchedResultController.objectAtIndexPath(self.selectedRowIndex!) as! Folder 

      if(folder.isprotected == true){ 
       let alert = UIAlertController(title: "Password Validation", message: "Please enter the folder password", preferredStyle: .Alert) 

       //2. Add the text field. You can configure it however you need. 
       alert.addTextFieldWithConfigurationHandler({ (textField) -> Void in 
        textField.text = "" 
       }) 

       alert.addAction(UIAlertAction(title: "Login", style: .Default, handler: { (action) -> Void in 
        let textField = alert.textFields![0] 
        if (folder.password == textField.text){ 
         response = true 
          self.performSegueWithIdentifier("listOcassions", sender: self) 
        } else { 
         let wrongPasswordAlert = UIAlertController(title: "Error", message: "Invalid password.", preferredStyle: UIAlertControllerStyle.Alert) 

         let dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction) -> 
          Void in 
         } 

         wrongPasswordAlert.addAction(dismiss) 

         self.presentViewController(wrongPasswordAlert, animated: true, completion: nil) 
         response = false 

        } // else if (folder.password == textField.text) 

       })) 

       // 4. Present the alert. 
       self.presentViewController(alert, animated: true, completion: nil) 

      } //if folder.isprotected == true 

     } 

    return response 
} 
} 

Я могу добавить записи, но при попытке удалить запись, я получаю следующее сообщение об ошибке:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete row 0 from section 0 which only contains 0 rows before the update' 

Я знаю, что есть аналогичные вопросы, подобные этому, но ни один из ответов на эти вопросы (которые я применил, а некоторые из них находятся в скопированном коде) не разрешил мою проблему.

Большое спасибо.

Julio.

ответ

0

Метод deleteObject в вашем методе commitEditingStyle: вызывает методы делегата FRC, которые затем удаляют соответствующую строку из представления таблицы. Таким образом, вы можете удалить эту строку:

self.myFoldersTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) 

из метода commitEditingStyle - это эффективно удалить ту же строку дважды. Отсюда и ошибка.

Аналогичным образом для editActionsForRowAtIndexPath.

Смежные вопросы