将“didMoveCellFromIndexPathToIndexPathBlock”与核心数据一起使用
Posted
技术标签:
【中文标题】将“didMoveCellFromIndexPathToIndexPathBlock”与核心数据一起使用【英文标题】:Using 'didMoveCellFromIndexPathToIndexPathBlock' with Core Data 【发布时间】:2015-07-11 13:45:32 【问题描述】:我有一个自定义的UITableView
,它负责重新排列单元格的动画,并且与不使用核心数据的标准测试数组完美配合。
在尝试使用核心数据与两个实体“文件夹”和“项目”以及To Many
关系的应用程序时,我收到一个错误。
[(Item)] 没有名为 exchangeObjectAtIndex 的成员
为;
tableView.didMoveCellFromIndexPathToIndexPathBlock = (fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) -> Void in
let delegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = delegate.managedObjectContext!
self.itemsArray.exchangeObjectAtIndex(toIndexPath.row, withObjectAtIndex: fromIndexPath.row)
var error: NSError? = nil
if !context.save(&error) abort()
这是因为:
NSSet、NSMutableSet 和 NSCountedSet 类声明 无序对象集合的编程接口。
所以我尝试将 NSSet 转换为 NSMutableArray 来管理对象顺序。
func itemsMutableArray() -> NSMutableArray
return NSMutableArray(array: (item.allObjects as! [Item]).sorted $0.date.compare($1.date) == NSComparisonResult.OrderedAscending )
然后我在我的表格视图中收到以下错误;这是因为可变数组是 AnyObject
,所以 swift 不相信它有 title 属性。
cell.textLabel?.text = folderMutableArray[indexPath.row].title
然后我回到我开始的地方。我只是想创建一个简单的列表并重新排列对象的顺序。
这是我的Folder
班级:
class Folder: NSManagedObject
@NSManaged var date: NSDate
@NSManaged var title: String
@NSManaged var item: NSSet
/// Method 1 - Converting to a NSMutableArray
// func itemsMutableArray() -> NSMutableArray
// return NSMutableArray(array: (item.allObjects as! [Item]).sorted $0.date.compare($1.date) == NSComparisonResult.OrderedAscending )
//
// Method 2 - creating an array of type Item
func itemArray() -> [Item]
let sortByDate = NSSortDescriptor(key: "Date", ascending: true)
return item.sortedArrayUsingDescriptors([sortByDate]) as! [Item]
生产力应用程序一直这样做,所以我知道这是可能的,但不确定如何,有人知道我哪里出错或有任何想法或建议吗?
【问题讨论】:
【参考方案1】:这很容易实现。您需要将名为index
的属性添加到您的Item
。将其设置为整数类型,并且每当您添加新的Item
时,将此值设置为您希望项目出现在其下的索引。这个属性应该作为NSManaged
属性添加到Core Data Model 和你的Item 的类中。
接下来,您需要向您的 Folder
类添加一个名为 arrayOfItems
的瞬态属性(当然,您可以将其重命名为您想要的任何名称)。在 Core Data Model 中将其设置为 Transient 和 Transformable。
在您的 Folder
班级中执行以下操作:
class Folder: NSManagedObject
@NSManaged var date: NSDate
@NSManaged var title: String
@NSManaged var item: NSSet
@NSManaged var arrayOfItems: [Items]
override func awakeFromFetch()
super.awakeFromFetch()
self.regenerateItems()
func regenerateItems()
let desc = NSSortDescriptor(key: "index", ascending: true)
if let array = item.sortedArrayUsingDescriptors([desc]) as? [Item]
self.arrayOfItems = array
现在,每当您获取Folder
的任何实例时,您都将获得一个正确排序且可变的Item
s 数组。您只需要考虑其他两种情况。
它们是由于 awakeFromFetch
仅在您从 Core Data 获取数据时才被调用。因此,您必须考虑其他情况。
添加新的Item
添加新的Item
时,您需要手动将新的Item
附加到arrayOfItems
,或者在完成添加新的Item
后调用regenerateItems()
。例如,假设您在代码的某处创建了初始数据:
var folder = NSEntityDescription.insertNewObjectForEntityForName("Folder", inManagedObjectContext: self.managedObjectContext!) as! Folder
var firstItem = NSEntityDescription.insertNewObjectForEntityForName("Item", inManagedObjectContext: self.managedObjectContext!) as! Item
// assuming that you have an inverse relationship from Item to Folder
// line below will assign it and will add your firstItem to Folder's
// NSSet of items
firstItem.folder = folder
// at this point your firstItem is added to your Folder but
// arrayOfItems is empty. So, you should call
folder.arrayOfItems = [firstItem]
// or you can call folder.regenerateItems()
上面的代码是指您创建初始数据时的情况。如果您在代码中的某处添加一个新的 Item
到已经有一些 Items
的文件夹中,则您有以下内容:
var newItem = NSEntityDescription.insertNewObjectForEntityForName("Item", inManagedObjectContext: self.managedObjectContext!) as! Item
// assuming that you have an inverse relationship from Item to Folder
// line below will assign it and will add your newItem to Folder's
// NSSet of items
newItem.folder = someExistingFolder
// at this point your newItem is added to your Folder but
// arrayOfItems is not yep updated and does not include newItem
// so you should either call
folder.arrayOfItems.append(newItem)
// or you can call folder.regenerateItems()
重新排列Items
此外,当您在表格视图中移动Item
s 时,您需要更改它们在arrayOfItems
中的索引和顺序。实现这一点的最简单方法可能是更改 arrayOfItems
中项目的顺序,然后遍历该数组,为其中的所有项目分配正确的新索引:
func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath)
let itemThatMoved = currentFolder.arrayOfItems[sourceIndexPath.row]
currentFolder.arrayOfItems.removeAtIndex(sourceIndexPath.row)
currentFolder.arrayOfItems.insert(itemThatMoved, atIndex:destinationIndexPath.row )
var currentIndex = 0
for item in currentFolder.arrayOfItems
item.index=currentIndex
currentIndex++
如果这有帮助或者您有任何其他问题,请告诉我。
附:我强烈建议您将关系名称从 item
更改为 items
。复数名称items
更符合NSSet
的逻辑。
【讨论】:
非常感谢您的回复,非常有用。按照您的建议进行了一些代码重构并更新了核心数据模型和类。我了解到目前为止的更新,但不确定将项目添加到文件夹时的含义。为什么我需要追加到 arrayOfItems 时创建一个 Items 数组?或者为什么我需要在之后致电regenerateItems()
?我问的原因是要完全理解代码,而不是盲目地更新代码希望得到结果。我用添加项目的代码更新了我的问题
给我几分钟,我将在我的回答中对此进行扩展
我会接受这个答案,非常感谢你的帮助,我真的很感激!信息量很大。仍在处理一个错误,当我移动一个单元格时,其他单元格将其名称更改为它们正在替换的单元格。例如。如果它们是 5 个单元格 - 1,2,3,4,5 并且我将 3 放在应该 5 而不是 1,2,4,5,3 的末尾,则其他单元格将其文本更新为替换单元格,例如1,2,3(4),4(5),3(移动单元格)
@JKSDEV 您是从 currentFolder.arrayOfItems 数组中填充单元格还是使用其他数组?我使用与我发布的代码相同的代码,并且在重新排列单元格时它可以正常工作,所以很奇怪它不适合你:(
我在填充单元格时直接使用selectedFolder.arrayOfItems
,例如cell.textLabel?.text = selectedFolder.arrayOfItems[indexPath.row].item
& returnCount = selectedFolder.arrayOfItems
以上是关于将“didMoveCellFromIndexPathToIndexPathBlock”与核心数据一起使用的主要内容,如果未能解决你的问题,请参考以下文章
Javascript 将正则表达式 \\n 替换为 \n,将 \\t 替换为 \t,将 \\r 替换为 \r 等等