更新核心数据对象顺序 - 不工作

Posted

技术标签:

【中文标题】更新核心数据对象顺序 - 不工作【英文标题】:Update Core Data Object Order - Not Working 【发布时间】:2015-08-09 17:45:34 【问题描述】:

当前行为

我的核心数据跟踪 UITableView 列表的值。每行都有一个标题和描述。我的核心数据正在用于添加新记录并稍后删除它们。核心数据也非常适合编辑现有记录的内容。

问题/疑问

我刚刚在表格中添加了拖放功能。从表面上看,它工作得很好。我可以将顶部的项目拖到底部、中间到顶部等。但是,新的列表顺序在应用关闭后不会保留,因为核心数据记录没有被更新。

我找到了一些关于此的教程,但似乎没有一个适用于我的代码。我还花了几个小时尝试使用和调整我当前的 Core Data 技能(更新、删除、编辑)来发明一个解决方案。我没有甜蜜的动作或代码功夫。

如果您选择接受此任务,以下是您可能需要的详细信息和代码。

信息

在 Swift 中编码

使用 X-Code 6.4

核心数据信息:

文件名是:CD_Model

实体名称:TodayTask

属性名称:“name”和“desc”

代码:

主列表变量:

var todayTaskList = [NSManagedObject]()

带有 UITableView 的主列表页面的 ViewDidLoad

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

    //Break

    //Load the list from Core Data
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext!
    let fetchRequest = NSFetchRequest(entityName:"TodayTask")
    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)")
    

    //Break

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

    //Break

    //Part of code for cell drag and drop functionality
    let longpress = UILongPressGestureRecognizer(target: self, action: "longPressGestureRecognized:")
    tableView.addGestureRecognizer(longpress)

表设置

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

这就是我的问题所在,拖放。此代码有效,但缺少重新排列核心数据的代码。没有该代码,当我缩小差距时,任何拖放重新排序都将重置:

   //This function initiates the Drag & Drop code.
    func longPressGestureRecognized(gestureRecognizer: UIGestureRecognizer) 

        let longPress = gestureRecognizer as! UILongPressGestureRecognizer
        let state = longPress.state

        var locationInView = longPress.locationInView(tableView)
        var indexPath = tableView.indexPathForRowAtPoint(locationInView)

        struct My 
            static var cellSnapshot : UIView? = nil
        
        struct Path 
            static var initialIndexPath : NSIndexPath? = nil
        

        let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as! CustomTableViewCell;

        var dragCellName = currentCell.nameLabel!.text
        var dragCellDesc = currentCell.descLabel.text


        //Steps to take a cell snapshot. Function to be called in switch statement
        func snapshopOfCell(inputView: UIView) -> UIView 
            UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
            inputView.layer.renderInContext(UIGraphicsGetCurrentContext())
            let image = UIGraphicsGetImageFromCurrentImageContext() as UIImage
            UIGraphicsEndImageContext()
            let cellSnapshot : UIView = UIImageView(image: image)
            cellSnapshot.layer.masksToBounds = false
            cellSnapshot.layer.cornerRadius = 0.0
            cellSnapshot.layer.shadowOffset = CGSizeMake(-5.0, 0.0)
            cellSnapshot.layer.shadowRadius = 5.0
            cellSnapshot.layer.shadowOpacity = 0.4
            return cellSnapshot
        


        switch state 
            case UIGestureRecognizerState.Began:
                //Calls above function to take snapshot of held cell, animate pop out
                //Run when a long-press gesture begins on a cell
                if indexPath != nil 
                    Path.initialIndexPath = indexPath
                    let cell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!
                    My.cellSnapshot  = snapshopOfCell(cell)
                    var center = cell.center

                    My.cellSnapshot!.center = center
                    My.cellSnapshot!.alpha = 0.0

                    tableView.addSubview(My.cellSnapshot!)

                    UIView.animateWithDuration(0.25, animations:  () -> Void in
                        center.y = locationInView.y

                        My.cellSnapshot!.center = center
                        My.cellSnapshot!.transform = CGAffineTransformMakeScale(1.05, 1.05)
                        My.cellSnapshot!.alpha = 0.98

                        cell.alpha = 0.0

                        , completion:  (finished) -> Void in

                            if finished 
                                cell.hidden = true
                            
                    )
                
            case UIGestureRecognizerState.Changed:
                //Runs when the user "lets go" of the cell
                //Sets CG Y-Coordinate of snapshot cell to center of current location in table (snaps into place)
                //If the indexPath is not 0 AND is not the same as it began (didn't move)...
                //Update array and table row order
                var center = My.cellSnapshot!.center
                center.y = locationInView.y
                My.cellSnapshot!.center = center
                if ((indexPath != nil) && (indexPath != Path.initialIndexPath)) 

                    swap(&todayTaskList[indexPath!.row], &todayTaskList[Path.initialIndexPath!.row])
                    tableView.moveRowAtIndexPath(Path.initialIndexPath!, toIndexPath: indexPath!)

                    Path.initialIndexPath = indexPath

                
            default:
                //Runs continuously as there's a long press recognized?
                //Animates cell movement
                //Completion block: 
                //Removes snapshot of cell, cleans everything up
                let cell = tableView.cellForRowAtIndexPath(Path.initialIndexPath!) as UITableViewCell!
                cell.hidden = false
                cell.alpha = 0.0
                UIView.animateWithDuration(0.25, animations:  () -> Void in
                    My.cellSnapshot!.center = cell.center
                    My.cellSnapshot!.transform = CGAffineTransformIdentity
                    My.cellSnapshot!.alpha = 0.0
                    cell.alpha = 1.0
                    , completion:  (finished) -> Void in
                        if finished 
                            Path.initialIndexPath = nil
                            My.cellSnapshot!.removeFromSuperview()
                            My.cellSnapshot = nil
                        
                )
        

我很确定我需要的代码会放在第二个 case 语句中:

case UIGestureRecognizerState.Changed:

我也认为我需要的代码应该以...开头。

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

但是有没有特殊的重新排列代码?我必须删除和插入吗?如果有,怎么做?

非常感谢任何可以帮助解决此问题的人!

【问题讨论】:

你需要为你的 tableview 使用 FetchedResultsController 以及某种索引来维护排序顺序(位置),然后当你拖放时你需要创建一个新对象(使用Core Data API's)具有正确的索引,您还需要更新其他对象索引(或排序顺序)。 TableView 将自动更新自身以正确反映更新对象的顺序。 我可以对“X-Code”感到震惊吗?哦,天哪……这比“xCode”更糟糕 【参考方案1】:

首先,您可能会发现为每个实体创建类更容易,这样您就不必处理模糊类型为NSManagedObject 或使用valueForKey(_:) 读取和转换的对象。在下面的解决方案中,我为此提供了代码示例。

所以要解决您的订单问题,您可以做两件事:

1) 添加定义Task 实体顺序的属性。这可以是一个简单的NSNumber,称为displayOrder。然后,您的 fetch 请求可以根据该属性对结果进行排序。然后,当重新排列表格单元格时,遍历任务列表并更新每个任务的displayOrder 属性以反映它们的显示顺序。保存您的托管对象上下文,下次加载获取请求时,它将相应地对它们进行排序。

class Task: NSManagedObject 
    @NSManaged var name: NSString
    @NSManaged var desc: NSString
    @NSManaged var displayOrder: NSNumber


let fetchRequest = NSFetchRequest()
let sortDescriptor = NSSortDescriptor(key: "displayOrder", ascending: true )
fetchRequest.sortDescriptors = [ sortDescriptor ]

2) 创建一个 CoreData 实体,该实体表示一个具有一对多关系的列表,该列表将每个任务实体存储在有序集中。然后,当您将任务添加到集合中时,它们将按照您添加它们的顺序保持保存。

class TaskList: NSManagedObject 
    @NSManaged var tasks: NSOrderedSet?


class Task: NSManagedObject 
    @NSManaged var name: NSString
    @NSManaged var desc: NSString
    @NSManaged var parentList: TaskList?

更新以回答剩余问题:

我强烈建议您使用自己的自定义类而不是 NSManagedObject,但在您弄清楚这部分内容之前,您可以按原样对代码执行操作。

在重新排列或删除后更新显示顺序:

func updateDisplayOrder() 
    for i in 0..<todayTaskList.count 
        let task = todayTaskList[i]
        task.setValue( i, forKey: "displayOrder" )
    

添加新任务:

func addTask( task: NSManagedObject, displayOrder: Int ) 
    todayTaskList.insert( task, atIndex: displayOrder )
    updateDisplayOrder()

【讨论】:

嗨帕特里克,谢谢。这听起来像是一个很好的解决方案。问题是我是 Core Data 的新手(截至三天前);我需要读一本书,但还没有。目前,我无法编写代码来包含订单属性并维护每条记录的正确值。你能举几个例子吗?例如,当我追加一个新对象时,我如何对核心数据列表进行计数,以便知道要为新对象分配什么值?或者我如何在列表中循环并在删除或重新排列后分配所有新值?一个例子将有很长的路要走。 这需要解释很多,我犹豫在这里添加它,因为它超出了这个问题的范围。但是,在读完一本 Core Data 书籍的几章之后,所有这些问题都会得到解答。 再一次,如果我在网上找不到代码答案,我可能会走书籍路线。我确实认为提供代码在“如果是,如何?”的范围内。问题的一部分。我发布了我的代码以显示我在做什么,并询问如何更改它,以便表格行顺序更改将持续存在。但是自己解决剩下的事情不会杀死我;期望论坛做所有的腿工作对我来说有点懒惰。感谢您的指导。当我最终弄清楚代码时,如果它符合您的高级解释,我会在那时将其标记为正确。 我添加了一个更新。你的代码需要做很多工作,有很多基本问题,忽略它们只是为了给你想要让它现在工作的 sn-ps,这让我很伤心。但是,被拒绝正确回答您的问题的分数会更加痛苦。所以,如果你愿意... 首先,感谢您附加建议的代码。我今天确实阅读了很多 Apple 的核心数据指南,但显然我仍然不是专家。如果您愿意澄清最后几个问题,我将不胜感激。我尝试将您的 updateDisplayOrder 添加到我的拖放代码(在长按手势更改时运行)并将 addTask/updateDisplayOrder 添加到我的“添加新列表项”代码中。我也添加了 context.save。但是关闭后拖放更改仍然不会持续存在。测试过程中没有错误。排序描述符 (fetchRequest.sortDescriptors = [ sortDescriptor ]) 去哪儿了?

以上是关于更新核心数据对象顺序 - 不工作的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法按照添加对象的时间顺序对核心数据进行排序? [复制]

删除 NSManagedObject 时的附加操作

按字母顺序排列的核心数据表

列表排序方法不更新顺序

可更新顺序记录的数据库设计

按插入顺序检索核心数据实体