(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
}
}
}
我有一个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
}
}
}