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

Tap Gesture引发Drag Gesture卡顿,如何保留Tap功能并优化流畅度?

保留Tap手势同时解决Drag手势卡顿问题

我实现了一个Contact Row View,点击该行可跳转至其他页面,左滑可显示后方的删除按钮。当前Drag Gesture初次拖动时存在明显卡顿(比动图表现更严重),但移除Tap Gesture后拖动操作变得流畅。请问如何在保留该行Tap功能的同时,让Drag Gesture保持流畅?

存在Tap Gesture时的卡顿状态

(初次左移和右移均出现跳帧)
卡顿状态

移除Tap Gesture后的流畅状态

流畅状态

相关代码

struct ContactRowView: View {
    
    @State var isSwiped: Bool = false
    @State var swipeOffset: CGFloat = 0
    
    var body: some View {
        ZStack {
            // 行后方背景
            Color.red
                .cornerRadius(8)
            
            // 删除按钮(位于行后方)
            HStack {
                Spacer()
                
                Button {
                    
                } label: {
                    Image(systemName: "trash")
                        .font(.callout)
                        .foregroundColor(Color.white)
                        .frame(width: 90, height: 50)
                }
            }
            
            // 联系人行内容
            HStack {
                Text("Name")
                    .foregroundColor(Color.white)
                
                Spacer()
            }
            .padding(.vertical)
            .padding(.horizontal)
            .background(
                Color.blue
                    .cornerRadius(8)
                    .shadow(color: Color.gray.opacity(0.1), radius: 4, x: 0, y: 2)
            )
            .offset(x: swipeOffset)
            .overlay(
                Color.clear
                    .contentShape(Rectangle())
                    .padding(.leading, 50)
                    .onTapGesture {
                        print("Tapped Row")
                    }
            )
            .gesture(DragGesture(minimumDistance: 0).onChanged(onChanged(value:)).onEnded(onEnd(value:)))
            
        }
        .frame(height: 44)
        .padding(.horizontal)
    }
    
    func onChanged(value: DragGesture.Value) {
        if value.translation.width < 0 {
            if isSwiped {
                swipeOffset = value.translation.width - 90
            } else {
                swipeOffset = value.translation.width
            }
        } else {
            if isSwiped {
                swipeOffset = value.translation.width - 90
            }
        }
    }
    
    func onEnd(value: DragGesture.Value) {
        withAnimation(.easeOut.speed(2)) {
            if value.translation.width < 0 {
                if -value.translation.width > UIScreen.main.bounds.size.width / 2 {
                    swipeOffset = -1000
                    print("DONE SWIPE")
                } else if -swipeOffset > 50 {
                    isSwiped = true
                    swipeOffset = -90
                } else {
                    isSwiped = false
                    swipeOffset = 0
                }
            } else {
                isSwiped = false
                swipeOffset = 0
            }
        }
    }
}

解决方案

卡顿的核心原因是Tap手势和Drag手势的识别冲突——系统需要先判断用户是要点击还是拖动,这个判断过程会导致初次拖动的延迟。以下是几种可行的解决方法:

方法1:使用simultaneousGesture替代gesture

将原来的.gesture(DragGesture...)替换为.simultaneousGesture(...),让两个手势可以同时被识别,跳过系统的冲突判断逻辑:

// 替换原有手势代码
.simultaneousGesture(DragGesture(minimumDistance: 0).onChanged(onChanged(value:)).onEnded(onEnd(value:)))

方法2:合并Tap和Drag逻辑

直接在Drag手势的结束回调中判断是否为点击操作(拖动距离极小),移除单独的Tap手势:

// 移除overlay中的onTapGesture,修改Drag手势逻辑
.gesture(DragGesture(minimumDistance: 0)
    .onChanged(onChanged(value:))
    .onEnded { value in
        // 判断是否为点击(拖动距离阈值可根据需求调整)
        if value.translation.width.magnitude < 10 && value.translation.height.magnitude < 10 {
            print("Tapped Row")
        }
        // 原有拖动结束逻辑
        withAnimation(.easeOut.speed(2)) {
            if value.translation.width < 0 {
                if -value.translation.width > UIScreen.main.bounds.size.width / 2 {
                    swipeOffset = -1000
                    print("DONE SWIPE")
                } else if -swipeOffset > 50 {
                    isSwiped = true
                    swipeOffset = -90
                } else {
                    isSwiped = false
                    swipeOffset = 0
                }
            } else {
                isSwiped = false
                swipeOffset = 0
            }
        }
    }
)

方法3:调整手势优先级

给Drag手势设置更高优先级,让系统优先识别拖动操作:

// 为Drag手势添加高优先级Tap手势
.gesture(DragGesture(minimumDistance: 0)
    .onChanged(onChanged(value:))
    .onEnded(onEnd(value:))
    .highPriorityGesture(TapGesture().onEnded {
        print("Tapped Row")
    }, including: .all)

推荐使用方法1或方法2,这两种方式都能有效解决卡顿问题,同时完整保留Tap交互功能。

内容的提问来源于stack exchange,提问作者kerbelda

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 23:22:53