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
相关产品推荐
相关产品推荐

