You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 04:53:14