SwiftUI中Overlay遮挡按钮,需同时保留捏合/长按手势的方案问询
解决方案
要同时保留按钮点击和捏合/长按手势功能,核心是让UIKit手势识别器仅处理非按钮区域的触摸事件,不拦截按钮的点击。具体修改如下:
1. 给手势识别器添加代理,过滤按钮区域触摸
修改PinchZoomView类,让它遵守UIGestureRecognizerDelegate协议,实现代理方法来判断是否接收触摸事件:
class PinchZoomView: UIView, UIGestureRecognizerDelegate { // 添加协议 let minScale: CGFloat let maxScale: CGFloat var isPinching: Bool = false var scale: CGFloat = 1.0 let scaleChange: (CGFloat) -> Void let location: (CGPoint) -> Void private var longPressLocation = CGPoint.zero init(minScale: CGFloat, maxScale: CGFloat, currentScale: CGFloat, scaleChange: @escaping (CGFloat) -> Void, location: @escaping (CGPoint) -> Void) { self.minScale = minScale self.maxScale = maxScale self.scale = currentScale self.scaleChange = scaleChange self.location = location super.init(frame: .zero) // 初始化手势并设置代理 let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(pinch(gesture:))) pinchGesture.cancelsTouchesInView = false pinchGesture.delegate = self // 设置代理 let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPress(gesture:))) longPressGesture.delegate = self // 设置代理 addGestureRecognizer(pinchGesture) addGestureRecognizer(longPressGesture) } required init?(coder: NSCoder) { fatalError() } // 代理方法:判断是否接收触摸事件 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { // 捏合手势直接接收(双指操作不会和按钮点击冲突) if gestureRecognizer is UIPinchGestureRecognizer { return true } // 长按/单指操作:如果触摸到按钮(UIControl),则不接收 if let touchedView = touch.view, touchedView is UIControl { return false } // 通用判断:触摸到其他可交互视图时不接收 let hitView = hitTest(touch.location(in: self), with: nil) if hitView != self && hitView?.isUserInteractionEnabled == true { return false } return true } // ... 原有长按和捏合处理方法保持不变 }
2. 移除Overlay的allowsHitTesting(false)
修改PinchToZoom modifier,去掉allowsHitTesting(false),让手势识别器正常接收触摸:
struct PinchToZoom: ViewModifier { let minScale: CGFloat let maxScale: CGFloat @Binding var scale: CGFloat @State var anchor: UnitPoint = .center @State var isPinching: Bool = false @Binding var longPressLocation: CGPoint func body(content: Content) -> some View { ZStack { content .scaleEffect(scale, anchor: anchor) .animation(.spring(), value: isPinching) .overlay( PinchZoom(minScale: minScale, maxScale: maxScale, scale: $scale, isPinching: $isPinching, longPressLocation: $longPressLocation) // 移除这行:.allowsHitTesting(false) ) } } }
原理说明
- 捏合手势是双指操作,和按钮的单指点击完全不冲突,所以直接允许接收所有触摸。
- 长按/单指操作通过代理方法判断:如果触摸点落在按钮(SwiftUI Button底层是
UIControl)或其他可交互视图上,手势识别器就放弃该触摸事件,让按钮自己处理点击。 - 非按钮区域的长按和捏合手势仍能正常触发,实现地图缩放、平移和添加标记的功能。
内容的提问来源于stack exchange,提问作者DeveloperSammy
相关产品推荐
相关产品推荐

