UIPanGestureRecognizer如何检测手指是否在按钮frame范围内?
问题根因
- 单个
UIPanGestureRecognizer实例无法同时绑定到多个视图,代码中创建的同一个手势先后添加给a1、a2,最终只有最后一个添加的a2能响应手势,a1上拖动完全不会触发回调。 - 坐标参考系不匹配:通过
gesture.location(in: gesture.view)获取的触点坐标是相对于当前触发手势的视图的,而按钮的frame是相对于它们的共同父视图定义的,两个坐标系不一致,contains判断自然全部失效。 - 手势绑定在按钮上时,手势触发后会持续锁定初始触点所在的视图,就算手指滑到其他按钮区域,手势也不会把触点位置映射到其他按钮的坐标系下,无法跨按钮识别位置。
修正方案
核心调整逻辑:
- 将拖动手势直接添加到所有按钮的共同父视图(比如承载a1、a2的控制器view)上,保证整个拖动范围都能接收到手势事件。
- 统一坐标参考系:获取手势触点时,直接取相对于父视图的坐标,和按钮frame的参考系保持一致。
- 保留已触发按钮的记录逻辑,避免手指在同一个按钮上停留时重复触发动作。
修正后可直接运行的代码:
var triggeredButtons = NSMutableArray() @IBOutlet weak var a1: UIButton! @IBOutlet weak var a2: UIButton! override func viewDidLoad() { super.viewDidLoad() let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGestureMethod(_:))) // 手势添加到按钮的共同父视图,而非单个按钮 self.view.addGestureRecognizer(panGesture) } @objc func panGestureMethod(_ gesture: UIPanGestureRecognizer) { // 手势开始时清空已触发记录 if gesture.state == .began { triggeredButtons.removeAllObjects() } // 手势结束状态直接跳过判断 guard gesture.state != .ended, gesture.state != .cancelled, gesture.state != .failed else { return } // 获取和按钮frame同坐标系的触点位置 let touchPoint = gesture.location(in: self.view) let targetButtons = [a1!, a2!] for button in targetButtons { if !triggeredButtons.contains(button), button.frame.contains(touchPoint) { triggeredButtons.add(button) // 触发对应按钮的业务逻辑 button == a1 ? a1Tapped(button) : a2Tapped(button) } } }
可选优化
- 如果需要按钮随拖动显示高亮效果,可在触发按钮动作时设置
button.isHighlighted = true,同时记录上一个触发的按钮,手指离开后重置高亮状态。 - 如果按钮不是直接添加在控制器根view上,只需将
gesture.location(in:)的参数替换为按钮的直接父视图,保证坐标参考系统一即可。 - 如果需要支持手指来回滑动时重复触发按钮动作,可新增触点离开按钮的判断逻辑,将离开的按钮从已触发数组中移除。
内容的提问来源于stack exchange,提问作者Kristoffer Melen
相关产品推荐
相关产品推荐

