SwiftUI中实现List行拖拽并将条目设为目标行子项的问题
SwiftUI List 行内拖拽触发Drop回调的解决方案
问题根源
- 修饰符挂载位置错误:你将
onDrop、onDrag修饰符挂载在ForEach容器上,而非每个列表行元素上,List默认行交互会拦截拖拽事件,导致行上的Drop无法触发 - 类型标识符不匹配:
onDrag方法中typeIdentifier参数传了空值,和onDrop指定接收的UTType.text类型不对应,系统判定拖拽内容不匹配,不会触发回调 - 现有
DropDelegate未携带目标行信息,无法后续完成「将拖拽条目设为目标行子项」的业务逻辑
修复后完整代码
import SwiftUI import UniformTypeIdentifiers struct Item: Identifiable { let id = UUID() let title: String // 后续可新增children属性存储子项 // var children: [Item] = [] } struct EditListView: View { @State private var items: [Item] = [ Item(title: "Apple"), Item(title: "Banana"), Item(title: "Papaya"), Item(title: "Mango") ] var body: some View { VStack { List { ForEach(items) { item in Text(item.title) // 修饰符挂载到每个行元素上 .onDrag { // 统一用UTType.text作为类型标识,携带拖拽源的id NSItemProvider(item: item.id.uuidString as NSSecureCoding, typeIdentifier: UTType.text.identifier) } .onDrop(of: [UTType.text], delegate: ItemDropDelegate(targetItemId: item.id, items: $items)) } } Text("Drop Item Here..") .fontWeight(.heavy) .onDrop(of: [UTType.text], delegate: dropDelegate()) } } } // 自定义携带目标行信息的DropDelegate class ItemDropDelegate: DropDelegate { let targetItemId: UUID @Binding var items: [Item] init(targetItemId: UUID, items: Binding<[Item]>) { self.targetItemId = targetItemId self._items = items } func performDrop(info: DropInfo) -> Bool { // 读取拖拽源的id guard let itemProvider = info.itemProviders(for: [UTType.text]).first else { return false } itemProvider.loadObject(ofClass: NSString.self) { object, error in DispatchQueue.main.async { if let sourceIdStr = object as? String, let sourceId = UUID(uuidString: sourceIdStr) { print("拖拽成功:源条目ID \(sourceId),目标条目ID \(self.targetItemId)") // 这里可自行实现将源条目添加为目标条目的子项逻辑 } } } return true } } // 原来的全局DropDelegate保留即可 class dropDelegate: DropDelegate { func performDrop(info: DropInfo) -> Bool { print("drop success") return true } }
验证说明
修复后拖拽任意列表行到另一行上,控制台会打印对应的源id和目标id,已触发Drop回调。后续你只需要调整Item的数据结构,增加子项存储字段,在performDrop中对应修改数据源即可完成「设置子项」的需求。
内容的提问来源于stack exchange,提问作者unknown
相关产品推荐
相关产品推荐

