如何加速识别单击而不是双击?
Posted
技术标签:
【中文标题】如何加速识别单击而不是双击?【英文标题】:How to accelerate the identification of a single tap over a double tap? 【发布时间】:2015-04-04 20:37:38 【问题描述】:我有一个UITableView
行,我在其中添加了单击和双击手势:
let doubleTap = UITapGestureRecognizer(target: self, action: "doubleTap:")
doubleTap.numberOfTapsRequired = 2
doubleTap.numberOfTouchesRequired = 1
let singleTap = UITapGestureRecognizer(target: self, action: "singleTap:")
singleTap.numberOfTapsRequired = 1
singleTap.numberOfTouchesRequired = 1
singleTap.requireGestureRecognizerToFail(doubleTap)
tableView.addGestureRecognizer(doubleTap)
tableView.addGestureRecognizer(singleTap)
有没有办法缩短从第一次点击到手势识别器意识到这是单击而不是双击之间的时间?
我问这个是因为当我单击时,新的viewController
出现得很晚,给人一种应用程序滞后的感觉。
【问题讨论】:
不,你需要直接处理水龙头来改变时间 我该怎么做?这是“最佳实践”吗? 在单元格上不是微不足道的,可能您会使用实现触摸事件方法的自定义子视图。或者,将您的 UI 更改为不使用双击 【参考方案1】:我在link找到了答案
快速版本:
class UIShortTapGestureRecognizer: UITapGestureRecognizer
let tapMaxDelay: Double = 0.3
override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!)
super.touchesBegan(touches, withEvent: event)
delay(tapMaxDelay)
// Enough time has passed and the gesture was not recognized -> It has failed.
if self.state != UIGestureRecognizerState.Ended
self.state = UIGestureRecognizerState.Failed
delay(delay: Double, closure:()->())
:
class func delay(delay:Double, closure:()->())
dispatch_after(dispatch_time( DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), closure)
【讨论】:
不要忘记在定义UITapGestureRecognizer
子类的文件中添加import UIKit.UIGestureRecognizerSubclass
。 (来自***.com/a/26557518/341954)否则你会得到构建错误:“方法没有覆盖其超类中的任何方法”。【参考方案2】:
完整实现Markus's Swift 3 版本的eladleb's 原始解决方案。
创建子类文件UIShortTapGestureRecogninzer
import UIKit
import UIKit.UIGestureRecognizerSubclass
class UIShortTapGestureRecognizer: UITapGestureRecognizer
let tapMaxDelay: Double = 0.3 //anything below 0.3 may cause doubleTap to be inaccessible by many users
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent)
super.touchesBegan(touches, with: event)
DispatchQueue.main.asyncAfter(deadline: .now() + tapMaxDelay) [weak self] in
if self?.state != UIGestureRecognizerState.recognized
self?.state = UIGestureRecognizerState.failed
注意:添加 UIGestureRecognizer
时,只有 doubleTap 类型需要为 UIShortTapGestureRecognizer
和 singleTap.require(toFail: doubleTap)
是必需的。
func addBoth (views: UIView, selectorSingle: Selector, selectorDouble: Selector)
let doubleTap:UIShortTapGestureRecognizer = UIShortTapGestureRecognizer(target: self, action: selectorDouble)
doubleTap.numberOfTapsRequired = 2
views.addGestureRecognizer(doubleTap)
let singleTap:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: selectorSingle)
singleTap.numberOfTapsRequired = 1
singleTap.require(toFail: doubleTap)
views.addGestureRecognizer(singleTap)
【讨论】:
【参考方案3】:对于未来,由 Howard Yang 全面实施,以下是链接: https://github.com/oney/SingleDoubleTapGestureRecognizer
let tap = SingleDoubleTapGestureRecognizer(target: self, singleAction: Selector("singleTap"), doubleAction: Selector("doubleTap"))
tap.duration = 0.8
view.addGestureRecognizer(tap)
https://github.com/oney/SingleDoubleTapGestureRecognizer/blob/master/Pod/Classes/SingleDoubleTapGestureRecognizer.swift
//
// SingleDoubleTapGestureRecognizer.swift
// SingleDoubleTapGestureRecognizer
//
// Created by Howard Yang on 08/22/2015.
// Copyright (c) 2015 Howard Yang. All rights reserved.
//
import UIKit
public class SingleDoubleTapGestureRecognizer: UITapGestureRecognizer
var targetDelegate: SingleDoubleTapGestureRecognizerDelegate
public var duration: CFTimeInterval = 0.3
didSet
self.targetDelegate.duration = duration
public init(target: AnyObject, singleAction: Selector, doubleAction: Selector)
targetDelegate = SingleDoubleTapGestureRecognizerDelegate(target: target, singleAction: singleAction, doubleAction: doubleAction)
super.init(target: targetDelegate, action: Selector("fakeAction:"))
numberOfTapsRequired = 1
class SingleDoubleTapGestureRecognizerDelegate: NSObject
var target: AnyObject
var singleAction: Selector
var doubleAction: Selector
var duration: CFTimeInterval = 0.3
var tapCount = 0
init(target: AnyObject, singleAction: Selector, doubleAction: Selector)
self.target = target
self.singleAction = singleAction
self.doubleAction = doubleAction
func fakeAction(g: UITapGestureRecognizer)
tapCount = tapCount + 1
if tapCount == 1
delayHelper(duration, task:
if self.tapCount == 1
NSThread.detachNewThreadSelector(self.singleAction, toTarget:self.target, withObject: g)
else if self.tapCount == 2
NSThread.detachNewThreadSelector(self.doubleAction, toTarget:self.target, withObject: g)
self.tapCount = 0
)
typealias DelayTask = (cancel : Bool) -> ()
func delayHelper(time:NSTimeInterval, task:()->()) -> DelayTask?
func dispatch_later(block:()->())
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(time * Double(NSEC_PER_SEC))),
dispatch_get_main_queue(),
block)
var closure: dispatch_block_t? = task
var result: DelayTask?
let delayedClosure: DelayTask =
cancel in
if let internalClosure = closure
if (cancel == false)
dispatch_async(dispatch_get_main_queue(), internalClosure);
closure = nil
result = nil
result = delayedClosure
dispatch_later
if let delayedClosure = result
delayedClosure(cancel: false)
return result;
func cancel(task:DelayTask?)
task?(cancel: true)
【讨论】:
虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。【参考方案4】:我宁愿推荐使用canBePrevented(by:) 函数,它考虑了要执行的点击次数,并且不会运行你的双击手势识别器,除非第一个被识别/失败。 canBePrevented(by:)
【讨论】:
【参考方案5】:受 Howard Yang 的启发,Swift 5.1 使用 DispatchWorkItem:
public class SingleDoubleTapGestureRecognizer: UITapGestureRecognizer
var targetDelegate: SingleDoubleTapGestureRecognizerDelegate
public var timeout: TimeInterval = 0.3
didSet
self.targetDelegate.timeout = timeout
public init(target: AnyObject, singleAction: Selector, doubleAction: Selector)
targetDelegate = SingleDoubleTapGestureRecognizerDelegate(target: target, singleAction: singleAction, doubleAction: doubleAction)
super.init(target: targetDelegate, action: #selector(targetDelegate.recognizerAction(recognizer:)))
class SingleDoubleTapGestureRecognizerDelegate: NSObject
weak var target: AnyObject?
var singleAction: Selector
var doubleAction: Selector
var timeout: TimeInterval = 0.3
var tapCount = 0
var workItem: DispatchWorkItem?
init(target: AnyObject, singleAction: Selector, doubleAction: Selector)
self.target = target
self.singleAction = singleAction
self.doubleAction = doubleAction
@objc func recognizerAction(recognizer: UITapGestureRecognizer)
tapCount += 1
if tapCount == 1
workItem = DispatchWorkItem [weak self] in
guard let weakSelf = self else return
weakSelf.target?.performSelector(onMainThread: weakSelf.singleAction, with: recognizer, waitUntilDone: false)
weakSelf.tapCount = 0
DispatchQueue.main.asyncAfter(
deadline: .now() + timeout,
execute: workItem!
)
else
workItem?.cancel()
DispatchQueue.main.async [weak self] in
guard let weakSelf = self else return
weakSelf.target?.performSelector(onMainThread: weakSelf.doubleAction, with: recognizer, waitUntilDone: false)
weakSelf.tapCount = 0
用法:
let singleDoubleTapRecognizer = SingleDoubleTapGestureRecognizer(
target: self,
singleAction: #selector(handleTapGesture),
doubleAction: #selector(handleDoubleTapGesture)
)
【讨论】:
【参考方案6】:Nico 接受的答案的 Swift 5 实现。
class UIShortTapGestureRecognizer: UITapGestureRecognizer
var maximumTapLength: Double = 0.3
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent)
super.touchesBegan(touches, with: event)
delay(delay: maximumTapLength)
// Enough time has passed and the gesture was not recognized -> It has failed.
if self.state != .ended
self.state = .failed
func delay(delay:Double, closure:@escaping ()->())
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: closure)
【讨论】:
以上是关于如何加速识别单击而不是双击?的主要内容,如果未能解决你的问题,请参考以下文章