来源: https://stackoverflow.com/questions/62606907/swiftui-using-ondrag-and-ondrop-to-reorder-items-within-one-single-lazygrid

本文主要使用到了onDragonDrop方法,此外还有类似的onInsert

Dec-02-2020 17-19-44
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//
// SwiftUIView.swift
// Demo
//
// Created by Zr埋 on 2020/12/1.
//

import SwiftUI
import UniformTypeIdentifiers

struct GridData: Identifiable, Equatable {
let id: Int

static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.id == rhs.id
}
}

func getData() -> [GridData] {
var data: [GridData] = []
for i in 0..<100 {
data.append(GridData(id: i))
}
return data
}

struct SwiftUIView: View {
@State private var data = getData()
@State private var draggingData: GridData?

// 定义VGrid一行的数据, 这里的spacing是行间距
let columns: [GridItem] = [
GridItem(.flexible(minimum: 50, maximum: 200), spacing: 30),
GridItem(.flexible(minimum: 50, maximum: 200), spacing: 30),
GridItem(.flexible(minimum: 50, maximum: 200), spacing: 30),
]

var body: some View {
ScrollView {
// 使用LazyVGrid而不是List以实现复用Cell型List
// spacing为列间距, 给定了行视图
LazyVGrid(columns: columns, spacing: 30, content: /*@START_MENU_TOKEN@*/{
ForEach(data) { d in
Text(d.id.description)
.font(.headline)
.foregroundColor(.white)
.frame(width: 100, height: 100, alignment: .center)
.background(Color.pink)
// 设定即将下落的效果
.overlay(draggingData?.id == d.id ? Color.white.opacity(0.8) : Color.clear)
// 这里返回一个NSItemProvider
.onDrag({
self.draggingData = d
// 虽然这用的是这个, 但常常是使用URL来作为文件的标识符 例如 URL(...) as NSURL
return NSItemProvider(object: d.id.description as NSString)
})
// OnDrop就比较麻烦, 首先of参数接收一个能够接收拖放文件类型的表述符数组, delegate接收一个
// 遵循DropDelegate协议的对象, 我们可以通过重写DropDelegate中的函数来实现回调
.onDrop(of: [ UTType.text], delegate: DropRelocateDelegate(item: d, listData: $data, currentData: $draggingData))
}
}/*@END_MENU_TOKEN@*/)
}
// 这一个onDrop为了处理没有拖到data Item上的Item, 这些Item被认为是移到了ScrollView上,
// 所以在这里加上一个处理函数
.onDrop(of: [UTType.text], delegate: DropOutsideDelegate(currentData: $draggingData))
// 添加动画
.animation(.easeInOut)
.padding()
.background(Color.black)
.edgesIgnoringSafeArea(.bottom)
}
}

struct DropOutsideDelegate: DropDelegate {
@Binding var currentData: GridData?

func performDrop(info: DropInfo) -> Bool {
currentData = nil
return true
}
}

struct DropRelocateDelegate: DropDelegate {
let item: GridData
@Binding var listData: [GridData]
@Binding var currentData: GridData?

func dropEntered(info: DropInfo) {
// 对移动位置进行判断
if item != currentData {
let from = listData.firstIndex(of: currentData!)!
let to = listData.firstIndex(of: item)!
if listData[to].id != currentData!.id {
listData.move(fromOffsets: IndexSet(integer: from), toOffset: to > from ? to + 1: to)
}
}
}

func dropUpdated(info: DropInfo) -> DropProposal? {
return DropProposal(operation: .move)
}

func performDrop(info: DropInfo) -> Bool {
self.currentData = nil
return true
}

}

struct SwiftUIView_Previews: PreviewProvider {
static var previews: some View {
SwiftUIView()
}
}

参考:

https://swiftui-lab.com/drag-drop-with-swiftui/

https://swiftwithmajid.com/2020/04/01/drag-and-drop-in-swiftui/