SwiftUI中onMove与LongPressGesture共存问题:拖拽与长按菜单冲突
解决方案
你的问题核心是给汉堡图标添加的LongPressGesture拦截了List默认的拖拽手势,导致onMove无法触发。同时需要实现:汉堡图标区域长按不弹出上下文菜单,行其他区域长按弹出菜单,且汉堡图标支持拖拽排序。
下面提供两种可行方案:
方案一:利用编辑模式+手势共存(实现简单)
通过强制List进入编辑状态(隐藏删除按钮),同时让汉堡图标的长按手势与拖拽手势共存,既标记长按状态以禁用上下文菜单,又不阻止拖拽触发onMove。
struct ContentView: View { @State var items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"] @GestureState var isHoldingHamburger = false var body: some View { List { ForEach(items, id: \.self) { item in HStack { Text(item) Spacer() Image(systemName: "line.3.horizontal") .gesture( LongPressGesture() .updating($isHoldingHamburger) { value, state, _ in state = value } // 允许长按手势与拖拽手势同时响应,不拦截拖拽 .simultaneously(with: DragGesture()) ) } // 仅当未长按汉堡图标时启用上下文菜单 .contextMenu(enabled: !isHoldingHamburger) { Text("Menu Item 1") Text("Menu Item 2") } // 隐藏编辑模式下的删除按钮 .listRowActions(.hidden) } .onMove(perform: move) // 强制List进入编辑状态,支持拖拽排序 .environment(\.editMode, .constant(.active)) } } func move(from source: IndexSet, to destination: Int) { items.move(fromOffsets: source, toOffset: destination) } }
方案二:自定义拖拽逻辑(贴近播客原生体验)
无需依赖List的编辑模式,完全自定义拖拽手势逻辑,实现长按汉堡图标直接拖拽排序,行其他区域正常弹出上下文菜单,体验更接近苹果播客的交互。
struct ContentView: View { @State var items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"] @State var draggedItem: String? @State var dragOffset: CGSize = .zero var body: some View { List { ForEach(items, id: \.self) { item in HStack { Text(item) Spacer() Image(systemName: "line.3.horizontal") .gesture( DragGesture() .onChanged { gesture in // 记录开始拖拽的项目 if draggedItem == nil { draggedItem = item } dragOffset = gesture.translation } .onEnded { gesture in guard let draggedItem = draggedItem else { return } // 计算拖拽源索引 guard let sourceIndex = items.firstIndex(of: draggedItem) else { return } // 根据拖拽偏移计算目标位置(假设行高为44) let rowHeight: CGFloat = 44 let moveRows = Int(gesture.translation.height / rowHeight) let destinationIndex = max(0, min(items.count - 1, sourceIndex + moveRows)) // 执行排序移动 if sourceIndex != destinationIndex { let targetOffset = destinationIndex > sourceIndex ? destinationIndex + 1 : destinationIndex items.move(fromOffsets: [sourceIndex], toOffset: targetOffset) } // 重置拖拽状态 self.draggedItem = nil dragOffset = .zero } ) } .contextMenu { Text("Menu Item 1") Text("Menu Item 2") } // 拖拽时显示视觉偏移效果 .offset(draggedItem == item ? dragOffset : .zero) .opacity(draggedItem == item ? 0.5 : 1.0) } } } }
内容的提问来源于stack exchange,提问作者Bernard Bijoch
相关产品推荐
相关产品推荐

