更新核心数据对象顺序 - 不工作
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 ]) 去哪儿了?以上是关于更新核心数据对象顺序 - 不工作的主要内容,如果未能解决你的问题,请参考以下文章