SwiftUI开发中如何无需EditMode即可实现列表项拖动排序
实现方案:使用onDrag + onDrop自定义拖放逻辑
不需要依赖系统的EditMode和onMove修饰符,通过给每个列表项单独添加拖放交互即可实现无编辑状态下的自由排序,同时不会影响SwipeActions、按钮点击等原有列表功能。
完整实现代码
import SwiftUI struct ContentView: View { @State private var items = ["1", "2", "3", "4", "5"] // 记录当前拖动的项目标识 @State private var draggingItem: String? var body: some View { List { ForEach(items, id: \.self) { item in Text("Item \(item)") // 可正常添加SwipeActions,不受排序逻辑影响 .swipeActions(edge: .trailing) { Button(role: .destructive) { if let index = items.firstIndex(of: item) { items.remove(at: index) } } label: { Label("删除", systemImage: "trash") } } // 配置拖动源 .onDrag { draggingItem = item return NSItemProvider(object: item as NSString) } // 配置放置目标 .onDrop(of: [.text], delegate: DropViewDelegate( currentItem: item, items: $items, draggingItem: $draggingItem )) // 可选:添加拖动状态视觉反馈 .opacity(draggingItem == item ? 0.4 : 1) .scaleEffect(draggingItem == item ? 1.03 : 1) .animation(.easeInOut(duration: 0.2), value: draggingItem) } } .listStyle(.plain) } } // 自定义Drop代理处理排序逻辑 struct DropViewDelegate: DropDelegate { let currentItem: String @Binding var items: [String] @Binding var draggingItem: String? func performDrop(info: DropInfo) -> Bool { draggingItem = nil return true } func dropEntered(info: DropInfo) { guard let draggingItem = draggingItem, draggingItem != currentItem else { return } guard let fromIndex = items.firstIndex(of: draggingItem), let toIndex = items.firstIndex(of: currentItem) else { return } // 执行重排操作 if fromIndex != toIndex { withAnimation { items.move(fromOffsets: IndexSet(integer: fromIndex), toOffset: toIndex > fromIndex ? toIndex + 1 : toIndex) } } } }
核心特性说明
- 全程不需要开启EditMode,列表默认状态下长按任意位置即可触发拖动,无需点击系统自带的拖动把手
- SwipeActions、列表项内的按钮点击功能完全不受影响,和普通列表行为完全一致
- 可以自由调整拖动触发逻辑、动画效果、视觉反馈,适配不同的设计需求
自定义数据模型适配
如果你的列表使用自定义结构体作为数据源,只需要让结构体遵循Identifiable协议,将draggingItem的类型改为结构体的ID类型即可,整体排序逻辑不需要修改。
内容的提问来源于stack exchange,提问作者newb
相关产品推荐
相关产品推荐

