'SwiftUI LazyVGrid Drag and Drop animation bug
I have created a minimal working example of a LazyVGrid with drag and drop. If I move an item fast and move to the corner of the display and drop it there, the animation moves to the wrong place and disappears after some time. The actual item is at the correct position.
I have added my code below.
import SwiftUI
import UniformTypeIdentifiers
struct SameSizeView: View {
@Namespace private var animation
@State private var dragging: MyRect?
@State private var rects = [
MyRect(),
MyRect(),
MyRect(),
MyRect(),
MyRect(),
MyRect(),
MyRect(),
MyRect(),
MyRect(),
MyRect()
]
let columns = [
GridItem(
.fixed(UIScreen.main.bounds.width / 2),
spacing: MyRect.spacing,
alignment: .topTrailing
),
GridItem(
.fixed(UIScreen.main.bounds.width / 2),
spacing: MyRect.spacing,
alignment: .topLeading
)
]
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
LazyVGrid(columns: columns, alignment: .center, spacing: MyRect.spacing) {
ForEach(rects) { rect in
rect
.matchedGeometryEffect(id: rect.id, in: animation)
.onDrag {
self.dragging = rect
return NSItemProvider(object: rect.id.uuidString as NSString)
}
.onDrop(of: [UTType.text], delegate: MyDropDelegate(item: rect, listData: $rects, current: $dragging))
}
}
}
.animation(.default, value: rects)
}
}
struct MyRect: View, Identifiable, Equatable {
var id = UUID()
@State var color = Color.init(.sRGB, red: Double.random(in: 0...1), green: Double.random(in: 0...1), blue: Double.random(in: 0...1), opacity: 1)
static var spacing: CGFloat = 16
var body: some View {
Rectangle()
.fill(color)
.frame(
width: UIScreen.main.bounds.width / 2 - MyRect.spacing * 1.5,
height: 2 * 62 + MyRect.spacing
)
}
static func == (lhs: MyRect, rhs: MyRect) -> Bool {
return lhs.id == rhs.id
}
}
struct MyDropDelegate: DropDelegate {
let item: MyRect
@Binding var listData: [MyRect]
@Binding var current: MyRect?
func dropEntered(info: DropInfo) {
if item != current {
let from = listData.firstIndex(of: current!)!
let to = listData.firstIndex(of: item)!
if listData[to] != current! {
listData.move(fromOffsets: IndexSet(integer: from),
toOffset: to > from ? to + 1 : to)
UIImpactFeedbackGenerator(style: .light).impactOccurred()
}
}
}
func dropUpdated(info: DropInfo) -> DropProposal? {
return DropProposal(operation: .move)
}
func performDrop(info: DropInfo) -> Bool {
self.current = nil
return true
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|