My end goal is to have a maneuverable list (like swiftui's native list) that reorders the position of items while you drag them.
I am trying to allow dragging and dropping of items in a LazyVGrid, but unlike the answer in SwiftUI | Using onDrag and onDrop to reorder Items within one single LazyGrid?, I am using Core Data, and therefore my array of items is not an observable object and therefore cannot be passed as an @Binding
for easy reordering.
Here is what I have:
import SwiftUI
struct TopListTest: View {
@Environment(\.managedObjectContext) var context
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "order", ascending: true)])
var array: FetchedResults<Item> //An array of items pulled from Core Data
@State private var dragging: Item?
var body: some View {
NavigationView {
ZStack(alignment: .top) {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: .greatestFiniteMagnitude))]) {
ForEach(array) { item in
listItemView(item: item)
.onDrag {
self.dragging = item
return NSItemProvider(object: String(item.order) as NSString)
}
.onDrop(of: ["Item"], delegate: DragRelocateDelegate(item: item, current: $dragging))
}
}
}
}
}
}
}
struct DragRelocateDelegate: DropDelegate {
//Where I would like to pass Core Data array, this would only be a copy, however
var item: Item
@Binding var current: Item?
func performDrop(info: DropInfo) -> Bool {
if item != current {
let from = current!.order
let to = item.order
if from != to {
item.order = from
current!.order = to
}
}
return true
}
}
struct listItemView: View {
var item: Item
var body: some View {
HStack {
Text("\(item.order)")
Spacer()
Text(item.name ?? "")
}
}
}
This code makes a simple list of core data entity Item
which has an order
which is just an id/position number and a name
. This allows you to drag and drop items but it only swaps the position of two items and it does not automatically reorder as you drag like swiftui lists.