自定义UIGestureRecognizer双击识别时状态重置问题咨询
解决自定义UIGestureRecognizer双击识别时reset()重置状态的问题
我之前也踩过这个坑!问题根源在于你对UIGestureRecognizer的生命周期理解不到位——reset()方法会在手势识别周期结束(无论成功还是失败)时被系统自动调用,用来清理手势的状态。如果第一次点击后你直接让手势进入了结束状态,reset()就会把你的点击计数重置,导致第二次点击完全追踪不到之前的计数。
核心思路调整
系统自带的UITapGestureRecognizer处理双击的逻辑其实很巧妙:第一次点击后,它不会立即结束手势,而是保持在.possible状态,同时启动一个定时器等待第二次点击。如果在超时前收到第二次点击,就判定为双击;超时则判定为单击(或者直接放弃识别)。这样就能避免reset()过早清理状态。
完整实现代码
下面是调整后的自定义双击手势识别器,你可以直接参考使用:
import UIKit class CustomDoubleTapGesture: UIGestureRecognizer { // 记录点击次数 private var tapCount = 0 // 用来等待第二次点击的定时器 private var tapTimer: Timer? // 用系统默认的双击间隔,和系统手势体验保持一致 private let doubleTapInterval = UIGestureRecognizer.defaultDoubleTapInterval override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) { super.touchesBegan(touches, with: event) // 只处理单指点击,多直接标记识别失败 guard touches.count == 1 else { state = .failed return } // 如果已有定时器在运行,说明是第二次点击,先取消定时器 if let existingTimer = tapTimer { existingTimer.invalidate() tapTimer = nil } tapCount += 1 } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) { super.touchesEnded(touches, with: event) guard touches.count == 1 else { state = .failed return } switch tapCount { case 1: // 第一次点击,启动定时器等待第二次点击 tapTimer = Timer.scheduledTimer(withTimeInterval: doubleTapInterval, repeats: false) { [weak self] _ in guard let self = self else { return } // 超时后可以选择标记为单击结束,或者直接失败(根据你的需求调整) self.state = .ended self.tapCount = 0 } // 关键:保持手势处于possible状态,系统不会调用reset() state = .possible case 2: // 第二次点击,触发双击成功 state = .ended tapCount = 0 default: // 超过两次点击,标记识别失败 state = .failed tapCount = 0 } } override func reset() { super.reset() // 清理定时器和计数,避免内存泄漏 tapTimer?.invalidate() tapTimer = nil tapCount = 0 } override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) { super.touchesCancelled(touches, with: event) // 手势被取消时也要及时清理状态 tapTimer?.invalidate() tapTimer = nil tapCount = 0 state = .cancelled } }
使用示例
在你的ViewController里这样集成:
override func viewDidLoad() { super.viewDidLoad() let doubleTapGesture = CustomDoubleTapGesture(target: self, action: #selector(handleDoubleTap(_:))) view.addGestureRecognizer(doubleTapGesture) } @objc private func handleDoubleTap(_ gesture: CustomDoubleTapGesture) { if gesture.state == .ended { print("双击成功触发!") } }
关键注意点
- 保持
.possible状态:第一次点击后不要立即把状态设为.ended,否则系统会立刻调用reset()清空你的计数。 - 定时器内存管理:一定要用
weak self避免循环引用,同时在reset()和touchesCancelled()里主动清理定时器。 - 对齐系统体验:使用
UIGestureRecognizer.defaultDoubleTapInterval作为双击间隔,让用户的操作感受和系统自带手势一致。
内容的提问来源于stack exchange,提问作者jayOrange
相关产品推荐
相关产品推荐

