编辑核心数据列表项而不是添加

时间:2015-08-08 05:23:47

标签: swift uitableview core-data edit objectcontext

我的待办事项应用已经添加和删除(来自Core Data)功能。这很完美。我的问题是我添加编辑的尝试出现故障。

行为

我编写了它,所以如果用户点击表中的任务,它会将该任务的单元索引路径,标题和描述设置为变量,并将它们传递给模态ViewController进行编辑。模态视图显示,然后传递的变量填充文本字段。然后,用户可以编辑现有内容,点击保存,运行一些保存代码(我将在下面分享)。模态视图被取消,表格数据重新加载,单元格出现在之前的位置但具有更新的内容。这一切都有效。应用程序关闭/完全关闭并重新打开时会发生故障。突然原始任务又回来了但是编辑的副本会附加到列表的底部。

问题

为什么我的代码会执行此操作,如何才能使编辑后的标题和说明正确保存和加载?

信息

我的核心数据文件名是:CD_Model 我的实体名称是:TodayTask 我的属性名称是:1)“name”2)“desc”

代码

我包含了很多代码,以防错误出现在我不希望的地方。但是,我在两个片段的标题中添加了粗体,我认为这些片段会导致问题。只有在运行viewDidLoad时才会显示错误(下面的第一个代码段)。但错误实际上可能发生在最后一个片段,即保存功能。

进口&全局变量:

import UIKit
import CoreData

var todayTaskList = [NSManagedObject]()
var passingEdit = false

主要VC的声明和viewDidLoad,其中包含表格:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
//***** ----- ***** ------ ***** ----- ***** ----- *****
//Initial Setup
//***** ----- ***** ------ ***** ----- ***** ----- *****

@IBOutlet weak var tableView: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    //This loads the list from Core Data
    //1
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext!

    //2
    let fetchRequest = NSFetchRequest(entityName:"TodayTask")

    //3
    var error: NSError?
    let fetchedResults = managedContext.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObject]

    if let results = fetchedResults {
        todayTaskList = results
    } else {
        println("Could not fetch \(error), \(error!.userInfo)")
    }

    //This provides a variable height for each row
    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 80.0
}

创建表格的代码:

//***** ----- ***** ------ ***** ----- ***** ----- *****
//Table View & Cell Setup
//***** ----- ***** ------ ***** ----- ***** ----- *****
@IBOutlet weak var name_Label: UILabel!
@IBOutlet weak var desc_Label: UILabel!

//Tells the table how many rows it should render
//*Looks to the Core Data NSObject to count tasks
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return todayTaskList.count
}

//Creates the individual cells. If the above function returns 3, this runs 3 times
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    //Setup variables
    let cellIdentifier = "BasicCell"
    let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! CustomTableViewCell
    let task = todayTaskList[indexPath.row]

    //Create table cell with values from Core Data attribute lists
    cell.nameLabel!.text = task.valueForKey("name") as? String
    cell.descLabel!.text = task.valueForKey("desc") as? String

    //Make sure the row heights adjust properly
    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 80.0

    return cell
}

点击现有任务时运行代码:

//***** ----- ***** ------ ***** ----- ***** ----- *****
//Functions
//***** ----- ***** ------ ***** ----- ***** ----- *****

//Action: Edit list item on row tap
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    passingEdit = true

    performSegueWithIdentifier("modalToEditor", sender: nil)
}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "modalToEditor") && passingEdit == true {

        //Assign selection to a variable 'currentCell'
        let indexPath = tableView.indexPathForSelectedRow();
        let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as! CustomTableViewCell;

        //Set cell text into variables to pass to editor
        var cellNameForEdit = currentCell.nameLabel!.text
        var cellDescForEdit = currentCell.descLabel.text

        //Pass values to EditorView
        var editorVC = segue.destinationViewController as! EditorView;
        editorVC.namePassed = cellNameForEdit
        editorVC.descPassed = cellDescForEdit
        editorVC.indexOfTap = indexPath


    }
}

模态编辑器VC的声明,设置从主VC传递的变量:

class EditorView: UIViewController, UITextFieldDelegate {

//Declare outlets and vars
@IBOutlet var txtTask: UITextField!
@IBOutlet var txtDesc: UITextView!
@IBOutlet weak var addSave: UIButton!
@IBOutlet weak var cancel: UIButton!

var namePassed: String!
var descPassed: String!
var indexOfTap: NSIndexPath!

//Initial Functions
override func viewDidLoad() {
    super.viewDidLoad()

    self.txtTask.becomeFirstResponder()

    if passingEdit == true {
        txtTask.text = namePassed
        txtDesc.text = descPassed
        addSave.setTitle("Save", forState: UIControlState.Normal)
    }
    else {
        addSave.setTitle("Add", forState: UIControlState.Normal)
    }
}

按下保存按钮时运行功能:

func modRec(nameValue: String, descValue: String, indexPos: NSIndexPath) {

    //1
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext!

    //2
    let entity =  NSEntityDescription.entityForName("TodayTask", inManagedObjectContext: managedContext)
    let todayTask = NSManagedObject(entity: entity!, insertIntoManagedObjectContext:managedContext)

    //3
    todayTask.setValue(nameValue, forKey: "name")
    todayTask.setValue(descValue, forKey: "desc")

    //4
    var error: NSError?
    if !managedContext.save(&error) {
        println("Could not save \(error), \(error?.userInfo)")
    }
    //5
    todayTaskList[indexPos.row] = todayTask
    managedContext.save(nil)


}

4 个答案:

答案 0 :(得分:2)

modRec方法中,您创建了一个新的TodayTask对象:

let todayTask = NSManagedObject(entity: entity!, insertIntoManagedObjectContext:managedContext)

然后替换todayTaskList数组中相关索引处的对象:

todayTaskList[indexPos.row] = todayTask

但是您修改其值的上一个对象仍然存在。它不再在您的阵列中,但它仍然在Core Data存储中。当您重新加载表视图时,它使用todayTaskList数组来填充行,并且您可以看到您的期望。但是当您关闭并重新打开应用程序时,todayTaskList数组本身将通过从CoreData存储中获取来重建。因为旧的todayTask和新的EditorView都存在,所以它们都被添加到数组中,因此您的表视图会显示两者。

要解决此问题,我会稍微重新构建您的代码:

  1. 修改NSManagedObject视图控制器:不是为传递给它的每个属性都使用vars,而是使用var作为完整的prepareForSegue。然后,您可以使用该NSMO的属性填充文本字段。 (您需要相应地修改TodayTask代码。如果要添加新的modRec,请创建NSManagedObject,然后将其添加到数组中,然后再转到EditorView。
  2. 然后,在您的TodayTask方法中,您不需要插入新的todayTaskList对象,只需设置NSMO var的属性即可。
  3. 由于您修改了现有的NSMO,而不是插入新的NSMO,因此您无需替换todayTaskList数组中的对象。
  4. 由于您不再需要在EditorView内更新passingEdit数组,因此它不需要是全局的(作为一种良好实践,您应该尽可能避免这样做) 。它只能是主视图控制器中的var。 (也应该可以避免IKLE(I)=IKLE(J)成为全球性的。

答案 1 :(得分:1)

我在您的代码中进行了以下更改。 我在函数中添加了oldnamevale和olddescvalue作为参数,可用于谓词。你必须传递这些值。

func modRec(oldnameValue: String, olddescValue: String,newnameValue: String, newdescValue: String, indexPos: NSIndexPath) {

    var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
    var context: NSManagedObjectContext = appDel.managedObjectContext!

    var fetchRequest = NSFetchRequest(entityName: "TodayTask")
fetchRequest.predicate = NSPredicate(format: "name = %@", oldnameValue)

    if let fetchResults = appDel.managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [NSManagedObject] {
      if fetchResults.count != 0{

        var managedObject = fetchResults[0]
        managedObject.setValue(newdescValue, forKey: "desc")
        managedObject.setValue(newnameValue, forKey: "name")

      context.save(nil)
      }
   }

}

如果要使用name和desc执行查询,请在上面的代码中进行以下更改

let Predicate1 = NSPredicate(format: "name = %@", oldnameValue)
let Predicate2 = NSPredicate(format: "desc = %@", olddescValue)

var compound = NSCompoundPredicate.andPredicateWithSubpredicates([Predicate1!, Predicate2!])
fetchRequest.predicate = compound

希望这可能会有所帮助。

答案 2 :(得分:0)

我认为问题在于modRec功能。您应该使用谓词修改Core数据值。请查看下面给出的示例或This linkThis tutorial。它可能对你有帮助。

func saveLoginData(accessToken: String, userName: String) {
    var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
    var context: NSManagedObjectContext = appDel.managedObjectContext!

    var fetchRequest = NSFetchRequest(entityName: "LoginData")
    fetchRequest.predicate = NSPredicate(format: "userName = %@", userName)

    if let fetchResults = appDel.managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [NSManagedObject] {
       if fetchResults.count != 0{

          var managedObject = fetchResults[0]
          managedObject.setValue(accessToken, forKey: "accessToken")

          context.save(nil)
       }
   }
}

答案 3 :(得分:0)

1,您应该在tableview上实施FetchedResultsControllerDelegate方法,不要尝试管理自己的列表 - 这不是正确的做事方式,会导致问题和记录无法正常显示在你的用户界面。

  1. 您只需要更新检索到的原始实体的属性。您的保存代码正在创建新记录。
  2. 因此,在编辑器视图中添加一个属性,该属性设置为原始对象,而不是每个属性。

    现在,在用户点击保存的编辑视图中,只需更新原始任务对象的属性并调用ManagedObjectContext.Save() - 太容易了。

    ManagedObjectManagedObjectContext值传递给EditorView

    var editorVC = segue.destinationViewController as! EditorView;
    editorVC.task = selectedTask
    editorVC.moc = managedContext
    

    编辑视图保存 - 只需更新原始任务属性(ManagedObject)

           this.task.name = nameField.Text
           this.task.desc = descField.Text
    
           // That's it or commit to disk by calling MOC.Save
           try {
                this.moc.Save()
           }