UITapGestureRecognizer-タッチアップではなく、タッチダウンで機能するようにしますか?


84

私がtapイベントを使用しているのは非常に時間に敏感なので、ユーザーがタッチアップする必要はなく、ユーザーがタッチダウンするだけでUITapGestureRecognizerをアクティブ化できるかどうか知りたいですか?


1
それが役立つ場合、UITouchにはtouchesStartedメソッドがあります。しかし、あなたが尋ねたように、それはジェスチャーレコグナイザーを使用していません。
vqdave 2013年

回答:


139

カスタム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");
}

迅速な実装:

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

テストでは、それが複数回呼び出されることはないことが示されています。ドロップインの代替品として機能します。2つの呼び出しを切り替えることができます。


10
「UIGestureRecognizerSubclass.h」インポートの追加+1。いいね。
Sebastian Hojas 2013

3
touchesBegin、touchesMoved、touchesEndedメソッドでスーパーを呼び出すべきではありませんか?
neoneye 2014

3
@JasonSilberman忘れた#import <UIKit / UIGestureRecognizerSubclass.h>
LE SANG

これでダブルタップをどのように実装しますか?
バビカー2015年

1
@etayluzは、開始と終了に状態を割り当てています。次に、ハンドルの状態を確認します。カスタムレコグナイザーは、すべてを制御できることを意味します。
LE SANG 2015

184

UILongPressGestureRecognizerを使用して、minimumPressDuration0に設定しUIGestureRecognizerStateBeganます。状態中はタッチダウンのように機能します。

Swift4 +の場合

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
インターフェイスビルダー(私)が好きな人のために、minimumPressDurationをIBで設定することもできます(素晴らしいソリューションをありがとうRob)
tmr 2014年

ここでタッチアップを検出する方法はありますか?
CalZone 2014年

1
@CalZoneはい、ジェスチャーの状態を確認してくださいUIGestureRecognizerStateEnded
Rob Caraway

2
良い回避策+1。minimumPressDuration0thoにすることができます。
Valentin Radu

1
危険通常のタッチで、複数回呼び出すことができます。
Fattie 2017

23

スイフト(サブクラスなし)

これは、RobCarawayのObjective-Cの回答に似た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")
        }
    }
}

1
@richy、名前が押しのジェスチャ認識機能であるため、私にとってもハックのように感じましたが、この方法はビューサブクラス化するよりもはるかに簡単で、あなたが言ったように、うまく機能します。
suragch 2016

このソリューションにより、UIscrollViewサブクラスでスクロールの問題が発生します
SPatel 2018年

私はbaterのアプローチは、カスタムジェスチャー認識だと思う
SPatel

@Suragch私はこれで、lessingの答えで遭遇したのと同じ問題に遭遇しました。この答えは機能しますが、オブジェクトに指を置いてスワイプするとすぐにタッチだと思われるため、スワイプする機能が失われました。どうすれば両方を区別できますか?
ランスサマリア

1
@LanceSamaria、タップするよりも複雑なタッチ認識が必要な場合は、カスタムジェスチャ認識機能を使用します。
スラグチ

1

これは別の解決策です。UIControlのサブクラスを作成します。UIControlはUIViewのサブクラスであるため、ストーリーボードでもUIViewのように使用できます。

class TouchHandlingView: UIControl {
}

そしてそれにaddTarget:

@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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.