SwiftUI实现LongTapGesture后触发Drag/MagnificationGesture及缩放假死修复
问题原因
你的实现出现缩放手势挂起的核心问题是SwiftUI对多层嵌套组合手势的状态传递存在兼容问题:你将DragGesture与MagnificationGesture的互斥组合作为LongPressGesture的序列后续手势时,缩放手势的连续状态变更和结束信号无法正确向上层传递,最终导致手势状态卡死。
修复方案
改用状态变量控制手势生效权限的实现逻辑,规避多层嵌套组合手势的bug:
- 新增布尔状态变量标记长按是否触发完成
- 单独处理长按手势,触发后放开拖动、缩放手势的使用权限
- 仅对拖动和缩放手势做互斥处理,避免嵌套组合手势的状态传递异常
修复后完整代码
struct DraggableCircle: View { enum DragState { case inactive case pressing case draggingOrMagnifying(translation: CGSize, magnification: CGFloat) var translation: CGSize { switch self { case .inactive, .pressing: return .zero case .draggingOrMagnifying(let translation, _): return translation } } var magnification: CGFloat { switch self { case .inactive, .pressing: return 1 case .draggingOrMagnifying(_, let magnification): return magnification } } var isActive: Bool { switch self { case .inactive: return false case .pressing, .draggingOrMagnifying: return true } } var isDragging: Bool { switch self { case .inactive, .pressing: return false case .draggingOrMagnifying: return true } } } @GestureState var dragState = DragState.inactive @State var viewState = CGSize.zero @State var magState = CGFloat(1) // 新增标记:长按是否已经触发 @State private var longPressTriggered = false private let minimumLongPressDuration = 0.5 var body: some View { // 单独定义长按手势 let longPress = LongPressGesture(minimumDuration: minimumLongPressDuration) .onEnded { _ in longPressTriggered = true dragState = .pressing } // 定义拖动手势,仅长按触发后生效 let drag = DragGesture() .enabled(longPressTriggered) .updating($dragState) { value, state, _ in state = .draggingOrMagnifying(translation: value.translation, magnification: 1) } .onEnded { value in viewState.width += value.translation.width viewState.height += value.translation.height resetState() } // 定义缩放手势,仅长按触发后生效 let magnification = MagnificationGesture() .enabled(longPressTriggered) .updating($dragState) { value, state, _ in state = .draggingOrMagnifying(translation: .zero, magnification: value) } .onEnded { value in magState *= value resetState() } // 组合手势:长按 + 拖动/缩放互斥 let combinedGesture = longPress .sequenced(before: drag.exclusively(before: magnification)) .onEnded { _ in resetState() } return Circle() .fill(Color.blue) .overlay(dragState.isDragging ? Circle().stroke(Color.white, lineWidth: 2) : nil) .frame(width: clipToBounds(100 * magState * dragState.magnification), height: clipToBounds(100 * magState * dragState.magnification), alignment: .center) .offset( x: viewState.width + dragState.translation.width, y: viewState.height + dragState.translation.height ) .animation(nil) .shadow(radius: dragState.isActive ? 8 : 0) .animation(.linear(duration: minimumLongPressDuration), value: dragState.isActive) .gesture(combinedGesture) } // 统一重置状态 private func resetState() { longPressTriggered = false dragState = .inactive } func clipToBounds(_ m: CGFloat) -> CGFloat { if abs(m) > UIScreen.main.bounds.height { return UIScreen.main.bounds.height } else { return abs(m) } } }
核心修改点
- 新增
longPressTriggered状态变量控制拖动、缩放手势的生效权限,避免多层嵌套组合手势的状态传递问题 - 拆分各个手势的独立逻辑,每个手势单独处理
updating和onEnded回调,状态流转更清晰 - 新增统一的
resetState方法,所有手势结束后统一重置状态,避免状态残留导致的卡死问题
内容的提问来源于stack exchange,提问作者Paul B
相关产品推荐
相关产品推荐

