(Swift) 使用 NSFetchedResultsController 在 indexPathFOrSelectedRow 编辑保存的数据

(Swift) Edit saved data at indexPathFOrSelectedRow using NSFetchedResultsController

我有一个UITableViewController和一个普通的ViewController。在 VC(名为 nyTankning)中,用户将输入保存到 Core Data 数据库的信息。数据库加载并显示在UITableVieController中。

现在我希望能够编辑保存在数据库中的信息。我已经搜索了答案,但我没有找到任何有用的东西,因为我在关注 retrieving core data into tableview cells swift

之后使用了 NSFetchedResultsController

现在我在表ViewController中编写的代码如下所示:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.

    if segue.identifier == "update" {
        let indexPath = self.tableView.indexPathForSelectedRow()
        let selectedCell = fetchedResultsController?.indexPathForObject(indexPath!) as? NSManagedObject
        let nyTankning: nyTankningViewController = segue.destinationViewController as nyTankningViewController

        nyTankning.liter = selectedCell?.valueForKey("liter") as String
        nyTankning.kronor = selectedCell?.valueForKey("kronor") as String
        nyTankning.exisitingTankning = selectedCell


    }

但是我从Debugger那里了解到indexPath没有写好。

2015-01-16 17:14:22.207 TankApp[4232:241135] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSIndexPath objectID]: unrecognized selector sent to instance 0x7c274660'

编辑:这是整个 UITable 的完整代码ViewController:

import Foundation
import UIKit
import CoreData

class tankningarTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {

var firstTime = 0

let managedObjectContext: NSManagedObjectContext? = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext

var fetchedResultsController: NSFetchedResultsController?


override func viewDidLoad() {
    super.viewDidLoad()


    fetchedResultsController = NSFetchedResultsController(fetchRequest: allaTankningarFetchRequest(), managedObjectContext: managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
    fetchedResultsController?.delegate = self
    fetchedResultsController?.performFetch(nil)


}

func alertViewFirstTime() {
    let alertController = UIAlertController(title: "TankApp", message:
        "Du kan lägga till en ny tankning genom att trycka på plusknappen (+).", preferredStyle: UIAlertControllerStyle.Alert)
    alertController.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default,handler: nil))

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

func allaTankningarFetchRequest() -> NSFetchRequest {

    var fetchRequest = NSFetchRequest(entityName: "Tankningslista")
    let sortDescriptor = NSSortDescriptor(key: "datum", ascending: false)

    fetchRequest.predicate = nil
    fetchRequest.sortDescriptors = [sortDescriptor]
    fetchRequest.fetchBatchSize = 20

    return fetchRequest
}


override func viewDidAppear(animated: Bool) {


    if firstTime == 0 {
        alertViewFirstTime()
        firstTime++

    }
    else {
        return
    }
}

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

// MARK: - Table view data source

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

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete method implementation.
    // Return the number of rows in the section. 
    return fetchedResultsController?.sections?[section].numberOfObjects ?? 0
}


//MARK: NSFetchedResultsController Delegate Functions
func controllerWillChangeContent(controller: NSFetchedResultsController) {
    tableView.beginUpdates()
}
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {

    switch type {
    case NSFetchedResultsChangeType.Insert:
        tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Fade)
        break
    case NSFetchedResultsChangeType.Delete:
        tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Fade)
        break
    case NSFetchedResultsChangeType.Move:
        break
    case NSFetchedResultsChangeType.Update:
        break
    default:
        break
    }
}

override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if editingStyle == .Delete {
    }

    switch editingStyle {
    case .Delete:
        managedObjectContext?.deleteObject(fetchedResultsController?.objectAtIndexPath(indexPath) as Model)
        managedObjectContext?.save(nil)
    case .Insert:
        break
    case .None:
        break
    }

}

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {

    switch type {
    case NSFetchedResultsChangeType.Insert:
        tableView.insertRowsAtIndexPaths(NSArray(object: newIndexPath!), withRowAnimation: UITableViewRowAnimation.Fade)
        break
    case NSFetchedResultsChangeType.Delete:
        tableView.deleteRowsAtIndexPaths(NSArray(object: indexPath!), withRowAnimation: UITableViewRowAnimation.Fade)
        break
    case NSFetchedResultsChangeType.Move:
        tableView.deleteRowsAtIndexPaths(NSArray(object: indexPath!), withRowAnimation: UITableViewRowAnimation.Fade)
        tableView.insertRowsAtIndexPaths(NSArray(object: newIndexPath!), withRowAnimation: UITableViewRowAnimation.Fade)
        break
    case NSFetchedResultsChangeType.Update:
        tableView.cellForRowAtIndexPath(indexPath!)
        break
    default:
        break
    }
}

func controllerDidChangeContent(controller: NSFetchedResultsController) {
    tableView.endUpdates()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    // Configure the cell...

    let CellID: NSString = "Cell"

    var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(CellID) as UITableViewCell


    let fetchRequest = NSFetchRequest(entityName: "Tankningslista")

    if let cellContact = fetchedResultsController?.objectAtIndexPath(indexPath) as? Model {
        cell.textLabel?.text = "\(cellContact.datum)"
        cell.detailTextLabel?.text = "\(cellContact.liter) Liter, \(cellContact.kronor) Kronor, \(cellContact.literpris) Kronor/Liter"

    }

    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.
    return true
}
*/

/*
// 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
        tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
    } else if editingStyle == .Insert {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }    
}
*/

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

}
*/

/*
// 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
}
*/

// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
    if segue.identifier == "update" {
        let destination = segue.destinationViewController as nyTankningViewController
        let indexPath = self.tableView.indexPathForCell(sender as Model)

        destination.event = indexPath != nil ? fetchedResultsController.objectAtIndexPath(indexPath!) as? NSManagedObject : nil
    }
}

}

我是这样完成的,使用三元运算符来处理展开可选索引路径的情况,但是 if let 也可以避免由此可能发生的任何崩溃。另外,我建议使用 indexPathForCell 并使用 sender 作为单元格而不是 indexPathForSelectedRow.

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
    if segue.identifier == "SomeSegue" {
        let destination = segue.destinationViewController as SomeViewController
        let indexPath = self.tableView?.indexPathForCell(sender as SomeCell)

        destination.event = indexPath != nil ? fetchedResultsController?.objectAtIndexPath(indexPath!) as? NSManagedObject : nil
    }
}

使用此方法,您将整个托管对象传递给下一个视图控制器,您将在其中相应地处理它。如果您想通过此方法保持设置所有目的地 class 变量,您可以使用 if let 来解包可选的索引路径,如下所示:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
    if segue.identifier == "SomeSegue" {
        let destination = segue.destinationViewController as SomeViewController
        if let indexPath = self.tableView?.indexPathForCell(sender as SomeCell) {
            let object = fetchedResultsController?.objectAtIndexPath(indexPath) as? NSManagedObject
            destination.foo = object?.bar
        }
    }
}

See a sample GitHub project here.