如何基于Core Data实现在LazyVGrid中的拖拽排序功能?
Core Data + LazyVGrid 实现拖拽自动重排列表
你的问题核心是当前代码仅交换两个项目的order值,而原生SwiftUI列表会自动调整拖拽路径中所有中间项目的位置。以下是修改后的完整实现:
修改后的代码
import SwiftUI import CoreData struct TopListTest: View { @Environment(\.managedObjectContext) var context @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "order", ascending: true)]) var array: FetchedResults<Item> @State private var dragging: Item? var body: some View { NavigationView { ScrollView { LazyVGrid(columns: [GridItem(.adaptive(minimum: .greatestFiniteMagnitude))]) { ForEach(array) { item in listItemView(item: item) .opacity(dragging == item ? 0.5 : 1) // 拖拽时的视觉反馈 .onDrag { dragging = item return NSItemProvider(object: String(item.order) as NSString) } .onDrop(of: ["public.text"], delegate: DragRelocateDelegate( item: item, current: $dragging, context: context, allItems: Array(array) )) } } } } } } struct DragRelocateDelegate: DropDelegate { let item: Item @Binding var current: Item? let context: NSManagedObjectContext let allItems: [Item] func performDrop(info: DropInfo) -> Bool { guard let currentItem = current, currentItem != item else { return false } let fromOrder = currentItem.order let toOrder = item.order // 获取排序后的项目数组 let sortedItems = allItems.sorted { $0.order < $1.order } guard let fromIndex = sortedItems.firstIndex(of: currentItem), let toIndex = sortedItems.firstIndex(of: item) else { return false } // 根据拖拽方向调整中间项目的order if fromIndex < toIndex { // 向下拖拽,中间项目order减1 for index in fromIndex+1...toIndex { sortedItems[index].order -= 1 } } else { // 向上拖拽,中间项目order加1 for index in toIndex...fromIndex-1 { sortedItems[index].order += 1 } } // 设置拖拽项目的最终order currentItem.order = toOrder // 保存Core Data上下文 do { try context.save() } catch { print("保存失败: \(error)") } current = nil return true } // 拖拽进入时的临时视觉反馈(可选) func dropEntered(info: DropInfo) { guard let currentItem = current, currentItem != item else { return } // 这里可以添加临时位置交换的动画,进一步贴近原生体验 } } struct listItemView: View { var item: Item var body: some View { HStack { Text("\(item.order)") Spacer() Text(item.name ?? "") } .padding() .background(Color.gray.opacity(0.1)) .cornerRadius(8) } }
关键修改点
- 批量调整Order值:不再仅交换两个项目的order,而是根据拖拽方向(向上/向下),批量调整路径中所有中间项目的order,实现类似原生列表的自动重排效果。
- Core Data上下文保存:修改order后必须保存上下文,确保变更持久化。
- 视觉反馈优化:给拖拽中的项目添加透明度变化,提升交互体验。
- 参数传递优化:在
DragRelocateDelegate中传入完整的项目数组和Core Data上下文,用于计算和更新order。
注意事项
- 确保Core Data实体
Item中的order属性为Int32或Int类型,且允许修改。 onDrop的类型改为["public.text"],因为我们传递的是字符串类型的order值,和onDrag中的类型匹配。
内容的提问来源于stack exchange,提问作者Elixir12
相关产品推荐
相关产品推荐

