web-dev-qa-db-ja.com

UIPanGestureRecognizerで最初にタップされたポイントをキャプチャするにはどうすればよいですか?

ユーザーが画面上の線をトレースできるアプリがあります。私はUIPanGestureRecognizer内のポイントを記録することによってそうしています:

-(void)handlePanFrom:(UIPanGestureRecognizer *)recognizer
{
    CGPoint pixelPos = [recognizer locationInView:rootViewController.glView];
    NSLog(@"recorded point %f,%f",pixelPos.x,pixelPos.y);
}

それはうまくいきます。ただし、ユーザーがパンを開始する前に最初にタップしたポイントに非常に興味があります。しかし、上記のコードは、発生したポイントのみを示していますジェスチャはパンとして認識されました(対タップ)。

ドキュメントから、UIPanGestureRecognizerAPI内で最初にタップされた場所を特定する簡単な方法がないようです。 UIPanGestureRecognizer.h内にありますが、次の宣言が見つかりました。

CGPoint _firstScreenLocation;

...これはプライベートのように見えるので、運がありません。私は、UIGestureRecognizerシステムの外に出て、その最初にタップされたポイントをキャプチャすることを検討しています。ユーザーが実際にUIPanGestureを開始したことがわかったら、後でそれを参照します。でも、その道を行く前に、ここで聞いてみようと思いました。

24
todd412

パーティーに遅れましたが、実際には上記の何も質問に答えていないことに気づきました。実際、これを行う方法があります。 UIPanGestureRecognizerをサブクラス化し、以下を含める必要があります。

#import <UIKit/UIGestureRecognizerSubclass.h>

クラスを作成するObjective-CファイルまたはSwiftブリッジヘッダー。これにより、touchesBegan:withEventメソッドを次のようにオーバーライドできます。

class SomeCoolPanGestureRecognizer: UIPanGestureRecognizer {
    private var initialTouchLocation: CGPoint!

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) {
        super.touchesBegan(touches, withEvent: event)
        initialTouchLocation = touches.first!.locationInView(view)
    }
}

次に、プロパティinitialTouchLocationに、探している情報が含まれます。もちろん、私の例では、一連のタッチの最初のタッチが対象のタッチであると想定しています。これは、maximumNumberOfTouchesが1の場合に意味があります。対象のタッチを見つけるには、より高度な方法を使用することをお勧めします。

編集: Swift 5


import UIKit.UIGestureRecognizerSubclass

class InitialPanGestureRecognizer: UIPanGestureRecognizer {
  private var initialTouchLocation: CGPoint!

  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesBegan(touches, with: event)
    initialTouchLocation = touches.first!.location(in: view)
  }
}
25
John Lawrence

途中でリセットしない限り、 translationInView: を使用して開始位置を計算できるはずです。翻訳とタッチの現在の場所を取得し、それを使用してタッチの開始点を見つけます。

20

@ジョンローレンスはそれを正しく持っています。

Swift 3:

import UIKit.UIGestureRecognizerSubclass

class PanRecognizerWithInitialTouch : UIPanGestureRecognizer {
  var initialTouchLocation: CGPoint!

  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesBegan(touches, with: event)
    initialTouchLocation = touches.first!.location(in: view)
  }
}

サブクラスインスタンス(ハンドラー)からアクセスする場合、インスタンス変数initialTouchLocationをプライベートにすることはできないことに注意してください。

ハンドラーで、

  func handlePan (_ sender: PanRecognizerWithInitialTouch) {
    let pos = sender.location(in: view)

    switch (sender.state) {
    case UIGestureRecognizerState.began:
      print("Pan Start at \(sender.initialTouchLocation)")

    case UIGestureRecognizerState.changed:
      print("    Move to \(pos)")
9
Brent Faust

この方法を使用できます。

CGPoint point    = [gesture locationInView:self.view];
5
Mickael West

このメソッドに配置されたのと同じUIViewにあります。

//-----------------------------------------------------------------------
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    CGPoint point = [[[event allTouches] anyObject] locationInView:self];
NSLog(@"point.x ,point.y  : %f, %f",point.x ,point.y);
}

こちらのUIGestureRecognizerクラスリファレンスでそれを探してください: https://developer.Apple.com/library/ios/documentation/uikit/reference/UIGestureRecognizer_Class/Reference/Reference.html

1
hokkuk

また、UIPanGestureRecognizerのshouldBeginメソッドで値を読み取ろうとすると、ユーザーが少し移動した後(つまり、ジェスチャがパンを認識し始めたとき)にのみその場所が表示されることに気付きました。ただし、このパンジェスチャが実際にどこから始まったかを知っておくと、認識すべきかどうかを判断できるので非常に便利です。

UIGestureRecognizerビューをサブクラス化したくない場合は、次の2つのオプションがあります。

  1. UILongPressGestureRecognizer、および遅延を0に設定します
  2. UIPanGestureRecognizer、およびshouldReceiveTouchで開始点をキャプチャします

他のジェスチャ(タップ、ダブルタップなど)がある場合は、遅延が0の長押しジェスチャ認識機能によって他のジェスチャが正しく認識されないため、オプション2が必要になる可能性があります。

他のジェスチャを気にせず、パンを正しく機能させるだけの場合は、遅延なしでUILongPressGestureRecognizerを使用できます。手動で開始を追跡する必要がないため、メンテナンスが簡単になります。ポイント。


解決策1:UILongPressGestureRecognizer

対象:シンプル

悪い例:他のジェスチャーハンドラーでニースをプレイする

ジェスチャを作成するときは、必ず minimumPressDuration を_0_に設定してください。これにより、すべてのデリゲートメソッド(開始する必要があるなど)が最初のタッチを適切に受信するようになります。

UILongPressGestureRecognizerは 連続ジェスチャレコグナイザー (個別のジェスチャレコグナイザーではなく)であるため、 IGestureRecognizer.State.changed プロパティを処理することで、移動を処理できます。 UIPanGestureRecognizer(これは継続的なジェスチャ認識機能でもあります)。基本的にあなたは 2つのジェスチャーを組み合わせる です。

_let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(gestureHandler(_:))
gestureRecognizer.minimumPressDuration = 0
_

解決策2:UIPanGestureRecognizer

対象:他のジェスチャ認識機能とうまく連携する

悪い例:開始状態を保存するのにもう少し手間がかかります

手順:

  1. まず、デリゲートとして登録し、shouldReceiveTouchイベントをリッスンする必要があります。

  2. それが発生したら、touchポイントを変数(ジェスチャポイントではありません!)に保存します。

  3. 実際にジェスチャを開始するかどうかを決定するときは、この変数を読んでください。

_var gestureStartPoint: CGPoint? = nil

// ...

let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(gestureHandler(_:))
gestureRecognizer.delegate = self

// ...

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    gestureStartPoint = touch.location(in: self)
    return true
}
_

警告:開始位置を正確に取得する唯一の方法は前者であるため、touch.location(in: self)ではなくgestureRecognizer.location(in: self)を必ず読んでください。

これで、次のように、どこでもgestureStartPointを使用できます。

_func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    return isValidStartPoint(gestureStartPoint!)
}
_
0
Senseful