I've been wondering if there is any way to customize the preview image of the item that's being dragged when using onDrag within a LazyGrid?
As you might see, the preview image stays static, while all other items change their heights.
I would like it to have the smaller height as well.
Like so:
Since a rendering of that view is generated and used as the preview image at the very beginning of the dragging process, before the height of the items are set, I have no idea how one would realize that.
One idea would be to have a LongPressGesture before the onDrag gesture. That way we could set the height before onDrag starts, which would lead to a snapshot of the new height. The problem I am facing is to combine these gestures so that they function like one, without the need to release between them.
Maybe there is another way or I have misunderstood something?
Here is the full demo code:
import SwiftUI
import UniformTypeIdentifiers
struct GridData: Identifiable, Equatable {
let id: Int
}
//MARK: - Model
class Model: ObservableObject {
@Published var data: [GridData]
let columns = [
GridItem(.fixed(160)),
GridItem(.fixed(160))
]
init() {
data = Array(repeating: GridData(id: 0), count: 100)
for i in 0..<data.count {
data[i] = GridData(id: i)
}
}
}
//MARK: - Grid
struct ContentView: View {
@StateObject private var model = Model()
@State private var dragging: GridData?
@State private var onDrag = false
var body: some View {
ScrollView {
LazyVGrid(columns: model.columns, spacing: 32) {
ForEach(model.data) { d in
GridItemView(d: d, onDrag: $onDrag)
.overlay(dragging?.id == d.id ? Color.white.opacity(0.8) : Color.clear)
.onDrag {
self.onDrag = true
self.dragging = d
return NSItemProvider(object: String(d.id) as NSString)
}
.onDrop(of: [UTType.text], delegate: DragRelocateDelegate(item: d, listData: $model.data, current: $dragging, onDrag: $onDrag))
}
}
.animation(.easeInOut(duration: 0.3), value: model.data)
}
}
}
//MARK: - Drag and Drop
struct DragRelocateDelegate: DropDelegate {
let item: GridData
@Binding var listData: [GridData]
@Binding var current: GridData?
@Binding var onDrag: Bool
func dropEntered(info: DropInfo) {
if item != current {
let from = listData.firstIndex(of: current!)!
let to = listData.firstIndex(of: item)!
if listData[to].id != current!.id {
listData.move(fromOffsets: IndexSet(integer: from),
toOffset: to > from ? to + 1 : to)
}
}
}
func dropUpdated(info: DropInfo) -> DropProposal? {
return DropProposal(operation: .move)
}
func performDrop(info: DropInfo) -> Bool {
self.onDrag = false
self.current = nil
return true
}
}
//MARK: - GridItem
struct GridItemView: View {
var d: GridData
@Binding var onDrag: Bool
var body: some View {
VStack {
Text(String(d.id))
.font(.headline)
.foregroundColor(.white)
}
.frame(width: 160, height: onDrag ? 140 : 240)
.background(Color.green)
.animation(.easeInOut(duration: 0.6))
}
}
Thanks!