SwiftUI 重新排序列表中的 CoreData 对象 [英] SwiftUI reorder CoreData Objects in List
问题描述
我想更改从核心数据中检索对象的列表中的行顺序.移动行有效,但问题是我无法保存更改.我不知道如何保存更改后的 CoreData 对象的索引.
I want to change the order of the rows in a list that retrieves objects from the core data. Moving rows works, but the problem is that I can't save the changes. I don't know how to save the changed Index of the CoreData Object.
这是我的代码:
核心数据类:
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())
}
}
感谢您的帮助.
推荐答案
警告:下面的答案未经测试,尽管我在示例项目中使用了并行逻辑并且该项目似乎正在运行.
Caveat: the answer below is untested, although I used parallel logic in a sample project and that project seems to be working.
答案有几个部分.正如 Joakim Danielson 所说,为了保留用户的首选订单,您需要将订单保存在 CoreItem 类中.修改后的类看起来像:
There's a couple parts to the answer. As Joakim Danielson says, in order to persist the user's preferred order you will need to save the order in your CoreItem class. The revised class would look like:
public class CoreItem: NSManagedObject, Identifiable{
@NSManaged public var name: String
@NSManaged public var userOrder: Int16
}
第二部分是根据 userOrder
属性对项目进行排序.在初始化时,userOrder
通常默认为零,因此在 userOrder
中按 name
排序可能很有用.假设你想这样做,那么在 CoreItemList 代码中:
The second part is to keep the items sorted based on the userOrder
attribute. On initialization the userOrder
would typically default to zero so it might be useful to also sort by name
within userOrder
. Assuming you want to do this, then in CoreItemList code:
@FetchRequest( entity: CoreItem.entity(),
sortDescriptors:
[
NSSortDescriptor(
keyPath: CoreItem.userOrder,
ascending: true),
NSSortDescriptor(
keyPath:CoreItem.name,
ascending: true )
]
) var coreItems: FetchedResults<CoreItem>
第三部分是你需要告诉swiftui允许用户修改列表的顺序.正如您在示例中所示,这是通过 onMove
修饰符完成的.在该修饰符中,您可以执行以用户的首选顺序重新排列列表所需的操作.例如,您可以调用一个名为 move
的便利函数,因此修饰符将显示为:
The third part is that you need to tell swiftui to permit the user to revise the order of the list. As you show in your example, this is done with the onMove
modifier. In that modifier you perform the actions needed to re-order the list in the user's preferred sequence. For example, you could call a convenience function called move
so the modifier would read:
.onMove( perform: move )
您的 move
函数将被传递一个 IndexSet 和一个 Int.索引集包含 FetchRequestResult 中所有要移动的项目(通常只有一个项目).Int 表示它们应该移动到的位置.逻辑是:
Your move
function will be passed an IndexSet and an Int. The index set contains all the items in the FetchRequestResult that are to be moved (typically that is just one item). The Int indicates the position to which they should be moved. The logic would be:
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 )
}
}
技术提示:revisedItems中存储的items是classes(即通过引用),所以更新这些items必然会更新fetched结果中的items.@FetchedResults 包装器将使您的用户界面反映新订单.
Technical reminder: the items stored in revisedItems are classes (i.e., by reference), so updating these items will necessarily update the items in the fetched results. The @FetchedResults wrapper will cause your user interface to reflect the new order.
诚然,我是 SwiftUI 的新手.可能会有更优雅的解决方案!
Admittedly, I'm new to SwiftUI. There is likely to be a more elegant solution!
Paul Hudson(Hacking With Swift)有更多的细节.这是一个链接 有关在列表中移动数据的信息.这是一个 链接 用于在 SwiftUI 中使用核心数据(它涉及删除列表中的项目,但与 onMove
逻辑非常相似)
Paul Hudson (Hacking With Swift) has quite a bit more detail. Here is a link for info on moving data in a list. Here is a link for using core data with SwiftUI (it involves deleting items in a list, but is closely analogous to the onMove
logic)
这篇关于SwiftUI 重新排序列表中的 CoreData 对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!