ListlessiOS/Extensions/ItemListView+Drag.swift (3024B)
1import SwiftUI
2
3extension ItemListView {
4 func handleIOSDragChanged(itemID: UUID, point: CGPoint) {
5 guard let draggedID = draggedItemID,
6 var order = visualOrder,
7 let currentIndex = order.firstIndex(of: draggedID) else { return }
8
9 let draggedFrame = layoutStorage.draggedRowFrame
10 guard draggedFrame != .zero else { return }
11
12 let threshold = draggedFrame.height * 0.2
13
14
15 if currentIndex < order.count - 1 && point.y > draggedFrame.maxY + threshold {
16 order.swapAt(currentIndex, currentIndex + 1)
17 withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
18 setDragOrder(order)
19 }
20 revealAdjacentItem(order: order, draggedID: draggedID, fingerY: point.y)
21 return
22 }
23
24
25 if currentIndex > 0 && point.y < draggedFrame.minY - threshold {
26 order.swapAt(currentIndex, currentIndex - 1)
27 withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
28 setDragOrder(order)
29 }
30 revealAdjacentItem(order: order, draggedID: draggedID, fingerY: point.y)
31 return
32 }
33
34
35 revealAdjacentItem(order: order, draggedID: draggedID, fingerY: point.y)
36 }
37
38 private func revealAdjacentItem(order: [UUID], draggedID: UUID, fingerY: CGFloat) {
39 guard let idx = order.firstIndex(of: draggedID) else { return }
40
41 let now = CACurrentMediaTime()
42 guard now - layoutStorage.lastAutoScrollTime > 0.2 else { return }
43
44 let screenHeight = UIScreen.main.bounds.height
45 let edgeZone: CGFloat = 120
46
47 if fingerY > screenHeight - edgeZone, idx + 1 < order.count {
48 layoutStorage.lastAutoScrollTime = now
49 withAnimation(.easeInOut(duration: 0.2)) {
50 scrollPosition.scrollTo(id: order[idx + 1], anchor: .bottom)
51 }
52 } else if fingerY < edgeZone, idx > 0 {
53 layoutStorage.lastAutoScrollTime = now
54 withAnimation(.easeInOut(duration: 0.2)) {
55 scrollPosition.scrollTo(id: order[idx - 1], anchor: .top)
56 }
57 }
58 }
59
60 func commitIOSDrag() {
61 guard let draggedID = draggedItemID,
62 let order = visualOrder,
63 let finalIndex = order.firstIndex(of: draggedID) else {
64 clearDragState()
65 isDragging = false
66 return
67 }
68 do {
69 try store.moveItem(itemID: draggedID, toIndex: finalIndex)
70 clearDragState()
71 isDragging = false
72 } catch {
73 withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
74 clearDragState()
75 isDragging = false
76 }
77 presentStoreError(error)
78 }
79 }
80}