I implemented a simple drag and drop for reordering items in a VStack/Scrollview according to this Solution
I store the currently dragged item in a property called draggingItem
and set the opacity to 0 depending if it is nil or not.
When performDrop in the DropDelegate gets called I set draggingItem
back to nil to make the corresponding item visible again.
There are two scenarios where performDrop seems not to get called:
When the item was onDrag and then released in place without moving.
When the item does get released slightly offset the actual droparea.
This is causing that the item does not get visible again because draggingItem
does not get set to nil again.
Any Ideas for a better place for setting draggingItem
back to nil?
View:
struct ReorderingTestsView: View {
@State var draggingItem: BookItem?
@State var items: [BookItem] = [
BookItem(name: "Harry Potter"),
BookItem(name: "Lord of the Rings"),
BookItem(name: "War and Peace"),
BookItem(name: "Peter Pane")
]
var body: some View {
VStack{
ScrollView{
VStack(spacing: 10){
ForEach(items){ item in
VStack{
Text(item.name)
.padding(8)
.frame(maxWidth: .infinity)
}
.background(Color.gray)
.cornerRadius(8)
.opacity(item.id == draggingItem?.id ? 0.01 : 1) // <- HERE
.onDrag {
draggingItem = item
return NSItemProvider(contentsOf: URL(string: "\(item.id)"))!
}
.onDrop(of: [.item], delegate: DropViewDelegate(currentItem: item, items: $items, draggingItem: $draggingItem))
}
}
.animation(.default, value: items)
}
}
.padding(.horizontal)
}
}
DropViewDelegate:
struct DropViewDelegate: DropDelegate {
var currentItem: BookItem
var items: Binding<[BookItem]>
var draggingItem: Binding<BookItem?>
func performDrop(info: DropInfo) -> Bool {
draggingItem.wrappedValue = nil // <- HERE
return true
}
func dropEntered(info: DropInfo) {
if currentItem.id != draggingItem.wrappedValue?.id {
let from = items.wrappedValue.firstIndex(of: draggingItem.wrappedValue!)!
let to = items.wrappedValue.firstIndex(of: currentItem)!
if items[to].id != draggingItem.wrappedValue?.id {
items.wrappedValue.move(fromOffsets: IndexSet(integer: from),
toOffset: to > from ? to + 1 : to)
}
}
}
func dropUpdated(info: DropInfo) -> DropProposal? {
return DropProposal(operation: .move)
}
}
TestItem:
struct BookItem: Identifiable, Equatable {
var id = UUID()
var name: String
}