我想更改从核心数据中检索对象的列表中行的顺序。移动行是可行的,但是问题是我无法保存更改。我不知道如何保存CoreData对象的已更改索引。
这是我的代码:
核心数据类:
public class CoreItem: NSManagedObject, Identifiable{
@NSManaged public var name: String
}
extension CoreItem{
static func getAllCoreItems() -> NSFetchRequest <CoreItem> {
let request: NSFetchRequest<CoreItem> = CoreItem.fetchRequest() as! NSFetchRequest<CoreItem>
let sortDescriptor = NSSortDescriptor(key: "date", ascending: true)
request.sortDescriptors = [sortDescriptor]
return request
}
}
extension Collection where Element == CoreItem, Index == Int {
func move(set: IndexSet, to: Int, from managedObjectContext: NSManagedObjectContext) {
do {
try managedObjectContext.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
列表:
struct CoreItemList: View {
@Environment(\.managedObjectContext) var managedObjectContext
@FetchRequest(fetchRequest: CoreItem.getAllCoreItems()) var CoreItems: FetchedResults<CoreItem>
var body: some View {
NavigationView{
List {
ForEach(CoreItems, id: \.self){
coreItem in
CoreItemRow(coreItem: coreItem)
}.onDelete {
IndexSet in let deleteItem = self.CoreItems[IndexSet.first!]
self.managedObjectContext.delete(deleteItem)
do {
try self.managedObjectContext.save()
} catch {
print(error)
}
}
.onMove {
self.CoreItems.move(set: $0, to: $1, from: self.managedObjectContext)
}
}
.navigationBarItems(trailing: EditButton())
}.navigationViewStyle(StackNavigationViewStyle())
}
}
谢谢您的帮助。
注意:下面的答案未经测试,尽管我在示例项目中使用了并行逻辑,并且该项目似乎正在运行。
答案有几个部分。如Joakim Danielson所说,为了保留用户的首选订单,您需要将订单保存在CoreItem类中。修改后的类如下所示:
public class CoreItem: NSManagedObject, Identifiable{
@NSManaged public var name: String
@NSManaged public var userOrder: Int16
}
第二部分是根据userOrder
属性对项目进行排序。初始化时,userOrder
通常默认为零,因此在name
中按userOrder
进行排序可能很有用。假设您要执行此操作,然后在CoreItemList代码中:
@FetchRequest( entity: CoreItem.entity(),
sortDescriptors:
[
NSSortDescriptor(
keyPath: \CoreItem.userOrder,
ascending: true),
NSSortDescriptor(
keyPath:\CoreItem.name,
ascending: true )
]
) var coreItems: FetchedResults<CoreItem>
第三部分是您需要告诉swiftui允许用户修改列表的顺序。如您的示例所示,这是通过onMove
修饰符完成的。在该修饰符中,您可以执行按照用户的首选顺序对列表重新排序所需的操作。例如,您可以调用一个名为move
的便利函数,因此修饰符应为:
.onMove( perform: move )
您的move
函数将被传递一个IndexSet和一个Int。索引集包含FetchRequestResult中所有要移动的项(通常只是一项)。 Int指示应将其移动到的位置。逻辑将是:
private func move( from source: IndexSet, to destination: Int)
{
// Make an array of items from fetched results
var revisedItems: [ CoreItem ] = coreItems.map{ $0 }
// change the order of the items in the array
revisedItems.move(fromOffsets: source, toOffset: destination )
// update the userOrder attribute in revisedItems to
// persist the new order. This is done in reverse order
// to minimize changes to the indices.
for reverseIndex in stride( from: revisedItems.count - 1,
through: 0,
by: -1 )
{
revisedItems[ reverseIndex ].userOrder =
Int16( reverseIndex )
}
}
[技术提醒:存储在modifiedItems中的项目是类(即,通过引用),因此更新这些项目将必然更新获取的结果中的项目。 @FetchedResults包装器将使您的用户界面反映新的顺序。
诚然,我是SwiftUI的新手。可能会有一个更优雅的解决方案!
Paul Hudson(Hacking With Swift)有很多细节。这是有关列表中移动数据的信息的link。这是在SwiftUI中使用核心数据的link(它涉及删除列表中的项目,但与onMove
逻辑极为相似)