SwiftUI 拖放重新排序 - 检测对象释放
SwiftUI Drag and Drop reorder - detect object release
我实现了一个简单的拖放操作,用于在 VStack/Scrollview 中重新排序项目
根据 this Solution
我将当前拖动的项目存储在名为 draggingItem
的 属性 中,并将不透明度设置为 0,具体取决于它是否为零。
当调用 DropDelegate 中的 performDrop 时,我将 draggingItem
设置回 nil 以使相应的项目再次可见。
有两种情况似乎没有调用 performDrop:
当项目被拖拽然后在没有移动的情况下释放到位。
当物品确实被释放时,实际的掉落区域会稍微偏移。
这导致项目不再可见,因为 draggingItem
没有再次设置为 nil。
关于将 draggingItem
设置回零的更好位置的任何想法?
查看:
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)
}
}
测试项目:
struct BookItem: Identifiable, Equatable {
var id = UUID()
var name: String
}
我调查了一个问题 1) 并在
中提出了解决方案
问题 2) 可以在自定义重写项提供程序和 deinit
上的操作的帮助下解决,`因为取消拖动会话时提供程序被销毁。
测试 Xcode 13.4 / iOS 15.5
主要部分:
// for demo simplicity, a convenient init can be created instead
class MYItemProvider: NSItemProvider {
var didEnd: (() -> Void)?
deinit {
didEnd?() // << here !!
}
}
// ...
let provider = MYItemProvider(contentsOf: URL(string: "\(item.id)"))!
provider.didEnd = {
DispatchQueue.main.async {
draggingItem = nil // << here !!
}
}
我实现了一个简单的拖放操作,用于在 VStack/Scrollview 中重新排序项目 根据 this Solution
我将当前拖动的项目存储在名为 draggingItem
的 属性 中,并将不透明度设置为 0,具体取决于它是否为零。
当调用 DropDelegate 中的 performDrop 时,我将 draggingItem
设置回 nil 以使相应的项目再次可见。
有两种情况似乎没有调用 performDrop:
当项目被拖拽然后在没有移动的情况下释放到位。
当物品确实被释放时,实际的掉落区域会稍微偏移。
这导致项目不再可见,因为 draggingItem
没有再次设置为 nil。
关于将 draggingItem
设置回零的更好位置的任何想法?
查看:
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)
}
}
测试项目:
struct BookItem: Identifiable, Equatable {
var id = UUID()
var name: String
}
我调查了一个问题 1) 并在
问题 2) 可以在自定义重写项提供程序和 deinit
上的操作的帮助下解决,`因为取消拖动会话时提供程序被销毁。
测试 Xcode 13.4 / iOS 15.5
主要部分:
// for demo simplicity, a convenient init can be created instead
class MYItemProvider: NSItemProvider {
var didEnd: (() -> Void)?
deinit {
didEnd?() // << here !!
}
}
// ...
let provider = MYItemProvider(contentsOf: URL(string: "\(item.id)"))!
provider.didEnd = {
DispatchQueue.main.async {
draggingItem = nil // << here !!
}
}