如何通过双击加速识别单个水龙头?

时间:2015-04-04 20:37:39

标签: ios uitableview swift uigesturerecognizer

我有一个带行的UITableView,我添加了单击手势和双击手势:

let doubleTap = UITapGestureRecognizer(target: self, action: "doubleTap:")
doubleTap.numberOfTapsRequired = 2
doubleTap.numberOfTouchesRequired = 1
tableView.addGestureRecognizer(doubleTap)

let singleTap = UITapGestureRecognizer(target: self, action: "singleTap:")
singleTap.numberOfTapsRequired = 1
singleTap.numberOfTouchesRequired = 1
singleTap.requireGestureRecognizerToFail(doubleTap)
tableView.addGestureRecognizer(singleTap)

有没有办法缩短第一次点击和手势识别器识别出是单击而不是双击之间的时间。

我问这个是因为当我点击一下时,新的viewController显得很晚,让人感觉应用程序滞后。

6 个答案:

答案 0 :(得分:5)

我在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)
    }

答案 1 :(得分:3)

Markus's原始解决方案的eladleb's Swift 3版本的完整实施。

创建子类文件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&amp; <{1}}是必需的。

singleTap.require(toFail: doubleTap)

答案 2 :(得分:1)

未来,由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)
    }
}

答案 3 :(得分:1)

我宁愿建议使用canBePrevented(by:)功能,该功能考虑了要执行的轻击次数,除非您的第一个手势识别器被识别/失败,否则它不会两次双击手势识别器运行。 canBePrevented(by:)

答案 4 :(得分:0)

受Howard Yang的实施启发,使用DispatchWorkItem的Swift 5.1:

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)
)

答案 5 :(得分:0)

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)
    }
}