UITapGestureRecognizer-使它在触地而不是触地工作吗?


84

我使用tap事件的时间非常敏感,因此我很好奇是否有可能在用户简单按下时激活UITapGestureRecognizer,而不是要求他们也向上触摸吗?


1
如果有帮助,则UITouch具有touchesStarted方法。但这并不是您要求的那样使用手势识别器。
vqdave

Answers:


139

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

TouchDownGestureRecognizer.h

#import <UIKit/UIKit.h>

@interface TouchDownGestureRecognizer : UIGestureRecognizer

@end

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

实施:

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

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

迅捷的实现:

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

这是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
    }
}

请注意,这是的替代品UITap。所以在像这样的代码中

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

您可以安全地交换到...。

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

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


10
“ UIGestureRecognizerSubclass.h”导入的额外+1。真好
塞巴斯蒂安·霍亚斯

3
超级不应该在touchesBegin,touchesMoved,touchesEnded方法中调用吗?
neoneye 2014年

3
@JasonSilberman,您忘了#import <UIKit / UIGestureRecognizerSubclass.h>
LE SANG

您将如何实现双击?
巴比克

1
@etayluz,您已经分配了开始和结束的状态。然后检查其状态是否在句柄中。自定义识别器意味着您可以控制所有内容。
LE SANG 2015年

183

使用UILongPressGestureRecognizer并将其设置minimumPressDuration为0。在UIGestureRecognizerStateBegan状态期间,它将像着陆一样。

对于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()
    }
}

对于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];
   }
}

4
对于喜欢界面构建器(me)的用户,还可以在IB中设置minimumPressDuration(感谢Rob提供出色的解决方案)
tmr 2014年

有没有办法检测到这里的修饰?
CalZone 2014年

1
@CalZone是的,检查手势状态UIGestureRecognizerStateEnded
Rob Caraway

2
一个不错的解决方法+1。minimumPressDuration可以是0 tho。
Valentin Radu

1
危险经常以典型的方式多次调用它。
Fattie

23

Swift(无子类)

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

这个想法是使用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")
        }
    }
}

1
@richy,由于名称是按手势识别器,所以对我来说也有点像黑客,但是此方法比将视图子类化要容易得多并且就像您说的那样,它很棒。
Suragch '16

此解决方案导致UIscrollView子类中的滚动问题
SPatel

我认为更轻松的方法是自定义手势识别器
-SPatel

@Suragch我遇到了与莱辛的回答同样的问题。这个答案有效,但是我失去了滑动的能力,因为一旦我将手指放在要滑动的对象上,它就会认为是触摸。如何区分两者?
Lance Samaria

1
@LanceSamaria,如果您需要更复杂的触摸识别,而不仅仅是点击,我会使用自定义手势识别器。
Suragch

1

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

class TouchHandlingView: UIControl {
}

并添加目标:

@IBOutlet weak var mainView: TouchHandlingView!
...

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

然后,将指定的动作称为UIButton:

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

1

我需要我的视图具有触发头发的功能,以便在它被点击时立即响应。同时使用@LESANG答案和使用@RobCaraway答案都可以。我在两个答案中遇到的问题是我失去了识别滑动的能力。滑动后我需要旋转视图,但是一旦我的手指触摸该视图,就只能识别出水龙头。tapRecognizer过于敏感,无法区分点击和滑动。

这是我根据@LESANG答案结合此答案和此答案得出的

我在每个事件中都发表了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
        }
    }
}

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

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 using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.