Ios 在swift 2.1中使用NSFetchedResultController删除行时出现问题

Ios 在swift 2.1中使用NSFetchedResultController删除行时出现问题,ios,swift,core-data,nsfetchedresultscontroller,Ios,Swift,Core Data,Nsfetchedresultscontroller,大家好,我正在使用NSFetchedResultController,但是当我删除一条记录(即使是第一条记录)时,我遇到了问题 以下是我的视图控制器的代码: import UIKit import CoreData class FoldersListViewController: UITableViewController, NSFetchedResultsControllerDelegate { @IBOutlet var myFoldersTableView: UITableView!

大家好,我正在使用NSFetchedResultController,但是当我删除一条记录(即使是第一条记录)时,我遇到了问题

以下是我的视图控制器的代码:

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.

您的
CommittedItingStyle:
方法中的
deleteObject
触发FRC委托方法,然后从表视图中删除相关行。因此,您可以删除此行:

self.myFoldersTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
committedingstyle
方法中,可以有效地删除同一行两次。因此出现了错误

同样地,对于
编辑操作的错误,XPath