如何启用多选,然后使用 ForEach(在 SwiftUI 中)移动/重新排序列表中的选定项目?
我尝试了以下代码。在 Mac 上它工作得很好 - 它允许我选择多个项目,然后将它们全部拖到列表中的另一个位置。在 iOS 上,它允许我通过拖放移动单个项目,并允许我进入编辑模式来选择多个项目,但是当我尝试通过拖放移动所选项目时,它们可以被拖动,但它们可以不要被删除到列表中的其他位置:
struct ExampleListView: View {
@State var items = ["Dave", "Tom", "Jeremy", "Luke", "Phil"]
@State var selectedItems: Set<String> = .init()
var body: some View {
NavigationView {
List(selection: $selectedItems) {
ForEach(items, id: \.self) { item in
Text(item).tag(item)
}.onMove(perform: move)
}
.navigationBarItems(trailing: EditButton())
}
}
func move(from source: IndexSet, to destination: Int) {
items.move(fromOffsets: source, toOffset: destination)
}
}
编辑 - 此代码具有相同的行为:
struct ExampleListView: View {
@State var items = ["Dave", "Tom", "Jeremy", "Luke", "Phil"]
@State var selectedItems: Set<String> = .init()
var body: some View {
NavigationView {
List($items, id: \.self, editActions: .all, selection: $selectedItems) { $item in
Text(item).tag(item)
}
.navigationBarItems(trailing: EditButton())
}
}
func move(from source: IndexSet, to destination: Int) {
items.move(fromOffsets: source, toOffset: destination)
}
}
实现此目的的一种方法是手动处理拖放交互。以下是如何修改代码以在 macOS 和 iOS 上启用多项目拖放功能
import SwiftUI
struct ExampleListView: View {
@State var items = ["Dave", "Tom", "Jeremy", "Luke", "Phil"]
@State var selectedItems: Set<String> = .init()
@State var draggedItems: [String] = []
var body: some View {
NavigationView {
List {
ForEach(items, id: \.self) { item in
Text(item)
.gesture(
DragGesture()
.onChanged { value in
if !self.selectedItems.contains(item) {
self.selectedItems = [item]
}
self.draggedItems = Array(self.selectedItems)
}
)
.onDrag {
NSItemProvider(object: NSString(string: item))
}
.onDrop(of: [UTType.text], delegate: MyDropDelegate(item: item, listItems: $items, selectedItems: $selectedItems, draggedItems: $draggedItems))
}
}
.navigationBarItems(trailing: EditButton())
}
}
}
struct MyDropDelegate: DropDelegate {
let item: String
@Binding var listItems: [String]
@Binding var selectedItems: Set<String>
@Binding var draggedItems: [String]
func performDrop(info: DropInfo) -> Bool {
let destinationIndex = self.listItems.firstIndex(of: item) ?? 0
var startIndex = 0
for i in 0..<listItems.count {
if draggedItems.contains(listItems[i]) {
let item = listItems.remove(at: i)
listItems.insert(item, at: destinationIndex + startIndex)
startIndex += 1
}
}
selectedItems = []
draggedItems = []
return true
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ExampleListView()
}
}