SwiftUI非List视图拖拽重排:如何让拖拽预览与原视图一致
SwiftUI拖拽重排(非List组件):修复拖拽预览异常问题
要实现拖拽预览与原视图完全一致,核心是自定义拖拽预览样式,覆盖系统默认的容器装饰,同时补全拖拽重排的完整逻辑(原代码仅实现了拖拽触发,缺少放置处理)。
问题根源
默认的.draggable预览会被系统添加默认的圆角背景和尺寸约束,导致预览与原视图的宽高、样式不匹配。
完整解决方案
import SwiftUI struct DragReorderView: View { @State private var items: [Color] = [.red, .blue, .green, .orange, .purple] @State private var draggingItem: Color? var body: some View { VStack(spacing: 16) { ForEach(items, id: \.self) { color in color .frame(height: 100) .cornerRadius(8) // 原视图的样式,预览需同步 // 自定义拖拽预览,完全匹配原视图 .draggable(color) { color .frame(height: 100) .cornerRadius(8) .frame(maxWidth: .infinity) // 强制宽度与父容器一致 .shadow(radius: 6) // 模拟拖拽悬浮效果(可选) .opacity(0.9) // 增加透明度提升自然感(可选) } // 处理放置逻辑,实现重排 .dropDestination(for: Color.self) { _, location in guard let draggedItem = draggingItem, let fromIndex = items.firstIndex(of: draggedItem) else { return false } // 计算放置目标位置 let toIndex = items.firstIndex { item in let itemFrame = getFrame(for: item) return location.y < itemFrame.midY } ?? items.count // 执行重排动画 if fromIndex != toIndex { withAnimation { items.move( fromOffsets: IndexSet(integer: fromIndex), toOffset: toIndex > fromIndex ? toIndex - 1 : toIndex ) } } draggingItem = nil return true } // 跟踪当前拖拽的元素 .onDrag { draggingItem = color return NSItemProvider(object: color.description as NSString) } } } .padding() } // 计算视图在全局坐标系中的位置,用于判断放置位置 private func getFrame(for color: Color) -> CGRect { guard let index = items.firstIndex(of: color) else { return .zero } let itemHeight: CGFloat = 100 let spacing: CGFloat = 16 let padding: CGFloat = 16 return CGRect( x: padding, y: padding + index * (itemHeight + spacing), width: UIScreen.main.bounds.width - padding * 2, height: itemHeight ) } }
关键细节说明
- 预览匹配:拖拽预览完全复制原视图的
frame、cornerRadius等样式,通过maxWidth: .infinity确保宽度与原视图一致,避免系统默认的窄化效果。 - 重排逻辑:通过
dropDestination监听放置事件,结合draggingItem跟踪拖拽元素,计算放置位置后调用move(fromOffsets:toOffset:)实现数组重排,配合withAnimation添加平滑过渡。 - 注意事项:如果存在重复颜色,建议改用自定义
Identifiable类型,避免firstIndex(of:)匹配错误:
struct ColoredItem: Identifiable { let id = UUID() let color: Color }
内容的提问来源于stack exchange,提问作者Nicolas Gimelli
相关产品推荐
相关产品推荐

