ios UITapGestureRecognizer - 让它在触地时工作,而不是触地?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15628133/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 22:52:46  来源:igfitidea点击:

UITapGestureRecognizer - make it work on touch down, not touch up?

iosobjective-cswiftuitapgesturerecognizer

提问by user212541

What I'm using the tap event for is very time-sensitive, so I'm curious if it's possible to make UITapGestureRecognizer activate when the user simply touches down, rather than requiring them to touch up as well?

我使用的点击事件对时间非常敏感,所以我很好奇是否有可能在用户触摸时激活 UITapGestureRecognizer,而不是要求他们也触摸?

回答by Rob Caraway

Use a UILongPressGestureRecognizer and set its minimumPressDurationto 0. It will act like a touch down during the UIGestureRecognizerStateBeganstate.

使用 UILongPressGestureRecognizer 并将其设置minimumPressDuration为 0。在该UIGestureRecognizerStateBegan状态期间,它的作用类似于触地。

For Swift 4+

Swift 4+

func setupTap() {
    let touchDown = UILongPressGestureRecognizer(target:self, action: #selector(didTouchDown))
    touchDown.minimumPressDuration = 0
    view.addGestureRecognizer(touchDown)
}

@objc func didTouchDown(gesture: UILongPressGestureRecognizer) {
    if gesture.state == .began {
        doSomething()
    }
}

For Objective-C

对于 Objective-C

-(void)setupLongPress
{
   self.longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(didLongPress:)];
   self.longPress.minimumPressDuration = 0;
   [self.view addGestureRecognizer:self.longPress];
}

-(void)didLongPress:(UILongPressGestureRecognizer *)gesture
{
   if (gesture.state == UIGestureRecognizerStateBegan){
      [self doSomething];
   }
}

回答by LE SANG

Create your custom TouchDownGestureRecognizer subclass and implement gesture in touchesBegan:

创建您的自定义 TouchDownGestureRecognizer 子类并在 touchesBegan 中实现手势:

TouchDownGestureRecognizer.h

TouchDownGestureRecognizer.h

#import <UIKit/UIKit.h>

@interface TouchDownGestureRecognizer : UIGestureRecognizer

@end

TouchDownGestureRecognizer.m

TouchDownGestureRecognizer.m

#import "TouchDownGestureRecognizer.h"
#import <UIKit/UIGestureRecognizerSubclass.h>

@implementation TouchDownGestureRecognizer
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    if (self.state == UIGestureRecognizerStatePossible) {
        self.state = UIGestureRecognizerStateRecognized;
    }
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    self.state = UIGestureRecognizerStateFailed;
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    self.state = UIGestureRecognizerStateFailed;
}


@end

implementation:

执行:

#import "TouchDownGestureRecognizer.h"
    TouchDownGestureRecognizer *touchDown = [[TouchDownGestureRecognizer alloc] initWithTarget:self action:@selector(handleTouchDown:)];
    [yourView addGestureRecognizer:touchDown];

-(void)handleTouchDown:(TouchDownGestureRecognizer *)touchDown{
    NSLog(@"Down");
}


Swift implementation:

斯威夫特实施:

import UIKit
import UIKit.UIGestureRecognizerSubclass

class TouchDownGestureRecognizer: UIGestureRecognizer
{
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent)
    {
        if self.state == .Possible
        {
            self.state = .Recognized
        }
    }

    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent)
    {
        self.state = .Failed
    }

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent)
    {
        self.state = .Failed
    }
}


Here is the Swift syntax for 2017 to paste:

这是 2017 年要粘贴的 Swift 语法:

import UIKit.UIGestureRecognizerSubclass

class SingleTouchDownGestureRecognizer: UIGestureRecognizer {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        if self.state == .possible {
            self.state = .recognized
        }
    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        self.state = .failed
    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
        self.state = .failed
    }
}

Note that this is a drop-in replacement for UITap. So in code like...

请注意,这是UITap. 所以在代码中...

func add(tap v:UIView, _ action:Selector) {
    let t = UITapGestureRecognizer(target: self, action: action)
    v.addGestureRecognizer(t)
}

you can safely swap to....

您可以安全地切换到....

func add(hairtriggerTap v:UIView, _ action:Selector) {
    let t = SingleTouchDownGestureRecognizer(target: self, action: action)
    v.addGestureRecognizer(t)
}

Testing shows it will not be called more than once. It works as a drop-in replacement; you can just swap between the two calls.

测试表明它不会被多次调用。它可以作为替代品;你可以在两个电话之间交换。

回答by Suragch

Swift (without subclassing)

Swift(没有子类化)

Here is a Swift version similar to Rob Caraway's Objective-C answer.

这是一个类似于Rob Caraway 的 Objective-C 答案的 Swift 版本。

The idea is to use a long press gesture recognizer with the minimumPressDurationset to zero rather than using a tap gesture recognizer. This is because the long press gesture recognizer reports touch began events while the tap gesture does not.

这个想法是使用minimumPressDuration设置为零的长按手势识别器,而不是使用点击手势识别器。这是因为长按手势识别器会报告触摸开始事件,而点击手势则不会。

import UIKit
class ViewController: UIViewController {

    @IBOutlet weak var myView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Add "long" press gesture recognizer
        let tap = UILongPressGestureRecognizer(target: self, action: #selector(tapHandler))
        tap.minimumPressDuration = 0
        myView.addGestureRecognizer(tap)
    }

    // called by gesture recognizer
    @objc func tapHandler(gesture: UITapGestureRecognizer) {

        // handle touch down and touch up events separately
        if gesture.state == .began {
            // do something...
            print("tap down")
        } else if gesture.state == .ended { // optional for touch up event catching
            // do something else...
            print("tap up")
        }
    }
}

回答by Lance Samaria

I needed the ability for my view to have a hair trigger so as soon as it's tapped it responds. Using both @LESANG answerworked and so did using @RobCaraway answer. The problem I ran into with bothanswers was I lost the ability to recognize swipes. I needed my view to rotate when swiped but as soon as my finger touched the view only the tap was recognized. The tapRecognizer was too sensitive and couldn't differentiate between a tap and a swipe.

我需要能够让我的视图有一个头发触发器,以便一旦它被点击它就会响应。使用@LESANG 答案和使用@RobCaraway 答案都有效。我在这两个答案中遇到的问题是我失去了识别滑动的能力。我需要我的视图在滑动时旋转,但是只要我的手指接触到视图,就只能识别出水龙头。tapRecognizer 过于敏感,无法区分轻敲和滑动。

This is what I came up with based off of @LESANG answercombined with this answerand this answer.

这是我根据@LESANG 的回答以及这个答案和这个答案提出的

I put 6 comments in each event.

我在每个事件中发表了 6 条评论。

import UIKit.UIGestureRecognizerSubclass

class SingleTouchDownGestureRecognizer: UIGestureRecognizer {


    var wasSwiped = false

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {

        guard let view = self.view else { return }
        guard let touches = event.touches(for: view) else { return } // 1. compare that event in touchesBegan has touches for the view that is the same as the view to which your gesture recognizer was assigned

        if touches.first != nil {
            print("Finger touched!") // 2. this is when the user's finger first touches the view and is at locationA
            wasSwiped = false // 3. it would seem that I didn't have to set this to false because the property was already set to false but for some reason when I didn't add this it wasn't responding correctly. Basically set this to false
        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {

        guard let touch = touches.first else { return }

        let newLocation = touch.location(in: self.view)
        let previousLocation = touch.previousLocation(in: self.view)

        if (newLocation.x > previousLocation.x) || (newLocation.x < previousLocation.x) {
            print("finger touch went right or left") // 4. when the user's finger first touches it's at locationA. If the the user moves their finger to either the left or the right then the finger is no longer at locationA. That means it moved which means a swipe occurred so set the "wasSwiped" property to true

            wasSwiped = true // 5. set the property to true because the user moved their finger
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
        print("finger is no longer touching.") // 6. the user has lifted their finger off of the view. If "wasSwiped" is true then ".fail" but if it wasn't swiped then ".recognize"

        if wasSwiped {
            self.state = .failed
        } else {
            self.state = .recognized
        }
    }
}

And to use it so that view that uses it gets the hair trigger response and left and right swipe gestures.:

并使用它,以便使用它的视图获得头发触发响应和左右滑动手势。:

let tapGesture = SingleTouchDownGestureRecognizer(target: self, action: #selector(viewWasTapped(_:)))
myView.addGestureRecognizer(tapGesture)

let rightGesture = UISwipeGestureRecognizer(target: self, action: #selector(respondToSwipeGesture(recognizer:)))
rightGesture.direction = .right
myView.addGestureRecognizer(rightGesture)

let leftGesture = UISwipeGestureRecognizer(target: self, action: #selector(respondToSwipeGesture(recognizer:)))
leftGesture.direction = .left
myView.addGestureRecognizer(leftGesture)

回答by morizotter

This is another solution. Create subclass of UIControl. You can use it like UIView even in Storyboard because UIControl is subclass of UIView.

这是另一种解决方案。创建 UIControl 的子类。即使在 Storyboard 中,您也可以像 UIView 一样使用它,因为 UIControl 是 UIView 的子类。

class TouchHandlingView: UIControl {
}

And addTarget to it:

并向其添加目标:

@IBOutlet weak var mainView: TouchHandlingView!
...

mainView.addTarget(self, action: "startAction:", forControlEvents: .TouchDown)
...

Then the designated action will be called like UIButton:

然后指定的动作会像 UIButton 一样被调用:

func startAction(sender: AnyObject) {
    print("start")
}