【问题标题】:UITapGestureRecognizer - make it work on touch down, not touch up?UITapGestureRecognizer - 让它在触地时工作,而不是触地?
【发布时间】:2013-03-15 17:18:06
【问题描述】:

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

【问题讨论】:

  • 如果有帮助,UITouch 有一个 touchesStarted 方法。但正如你所问的那样,这并没有使用手势识别器。

标签: ios objective-c swift uitapgesturerecognizer


【解决方案1】:

使用 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];
   }
}

【讨论】:

  • 对于喜欢界面构建器的人(我),minimumPressDuration 也可以在 IB 中设置(感谢 Rob 的出色解决方案)
  • 有没有办法检测到这里的修饰?
  • @CalZone 是的,检查手势状态UIGestureRecognizerStateEnded
  • 一个不错的解决方法 +1。 minimumPressDuration 可以是 0。
  • DANGER 通常可以多次调用它,并带有典型的触摸。
【解决方案2】:

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

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

Swift 实现:

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

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

【讨论】:

  • “UIGestureRecognizerSubclass.h”导入的额外 +1。不错。
  • 不应该在 touchesBegin,touchesMoved,touchesEnded 方法中调用 super 吗?
  • @JasonSilberman 你忘了#import
  • 你会如何用这个实现双击?
  • @etayluz 您已将状态指定为开始和结束。然后检查它在句柄中的状态。自定义识别器意味着您可以控制一切。
【解决方案3】:

Swift(没有子类化)

这是一个类似于Rob Caraway's Objective-C answer 的 Swift 版本。

我们的想法是使用长按手势识别器并将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")
        }
    }
}

【讨论】:

  • @richy,我也觉得有点像hack,因为名字是long按下手势识别器,但是这个方法比subclassing the view简单多了就像你说的,效果很好。
  • 此解决方案导致 UIscrollView 子类出现滚动问题
  • 我认为bater方法是自定义手势识别器
  • @Suragch 我遇到了与 lessing 的回答相同的问题。这个答案有效,但我失去了滑动的能力,因为只要我将手指放在物体上滑动它,它就会认为它是触摸。如何区分两者?
  • @LanceSamaria,如果您需要比点击更复杂的触摸识别,我会使用自定义手势识别器。
【解决方案4】:

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

class TouchHandlingView: UIControl {
}

并向其添加目标:

@IBOutlet weak var mainView: TouchHandlingView!
...

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

那么指定的action就会像UIButton一样被调用:

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

【讨论】:

    【解决方案5】:

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

    这是我根据@LESANG answer 结合answeranswer 得出的。

    我在每场比赛中放了 6 个 cmets。

    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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-23
      • 2020-07-04
      • 1970-01-01
      • 2011-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多