繁体   English   中英

如何捕获最初在 UIPanGestureRecognizer 中点击的点?

[英]How do I capture the point initially tapped in a UIPanGestureRecognizer?

我有一个应用程序,可以让用户在屏幕上跟踪线条。 我是通过在 UIPanGestureRecognizer 中记录点来实现的:

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

这很好用。 但是,我对用户在开始平移之前点击的第一点非常感兴趣。 但是上面的代码只给了我在手势被识别为平移(与轻敲)之后发生的点。

从文档来看,似乎没有简单的方法可以确定 UIPanGestureRecognizer API 中最初点击的位置。 虽然在 UIPanGestureRecognizer.h 中,我发现了这个声明:

CGPoint _firstScreenLocation;

...这似乎是私人的,所以没有运气。 我正在考虑完全脱离 UIGestureRecognizer 系统,只是为了捕获那个初始点击的点,然后在我知道用户确实开始了 UIPanGesture 时再参考它。 不过,在走那条路之前,我想我会在这里问。

聚会迟到了,但我注意到上面没有真正回答这个问题,事实上有一种方法可以做到这一点。 您必须继承 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 将包含您寻求的信息。 当然,在我的示例中,我假设触摸集中的第一个触摸是感兴趣的触摸,如果 maxNumberOfTouches 为 1,这是有道理的。您可能希望使用更复杂的方法来查找感兴趣的触摸。

编辑:斯威夫特 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)
  }
}

您应该能够使用translationInView:来计算起始位置,除非您在两者之间重置它。 获取触摸的翻译和当前位置,并使用它来找到触摸的起点。

@John Lawrence 说得对。

为 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)")

你可以使用这个方法:

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

在同一个 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

我还注意到,当我尝试读取 UIPanGestureRecognizer 上的 shouldBegin 方法中的值时,我只能在用户稍微移动一点后(即当手势开始识别平移时)才能看到它的位置。 知道这个平移手势实际上从哪里开始是非常有用的,这样我就可以决定它是否应该识别。

如果你不想继承 UIGestureRecognizer 视图,你有两个选择:

  1. UILongPressGestureRecognizer ,并将延迟设置为 0
  2. UIPanGestureRecognizer ,并在 shouldReceiveTouch 中捕获起点

如果您有其他手势(例如点击、双击等),那么您可能需要选项 2,因为延迟为 0 的长按手势识别器将导致其他手势无法正确识别。

如果您不关心其他手势,而只希望平底锅正常工作,那么您可以使用延迟为 0 的 UILongPressGestureRecognizer 并且更容易维护,因为您无需手动跟踪开始观点。


解决方案 1:UILongPressGestureRecognizer

适合:简单

不利于:与其他手势处理程序配合良好

创建手势时,请确保将minimumPressDuration设置为0 这将确保您的所有委托方法(例如应该开始)将正确接收第一次触摸。

因为 UILongPressGestureRecognizer 是一个连续手势识别器(与离散手势识别器相反),所以您可以通过处理UIGestureRecognizer.State.changed属性来处理移动,就像使用 UIPanGestureRecognizer(它也是一个连续手势识别器)一样。 本质上,您正在组合这两种手势

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

解决方案 2:UIPanGestureRecognizer

适用于:与其他手势识别器配合使用

不利于:保存开始状态需要更多的努力

步骤:

  1. 首先,您需要注册为委托并监听 shouldReceiveTouch 事件。

  2. 一旦发生这种情况,将触摸点保存在某个变量中(而不是手势点!)。

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

您可以使用 UIGestureRecognizerStateBegan 方法。 这是有关 UIGestureRecognizer 类的 Apple 文档的链接。

http://developer.apple.com/library/ios/ipad/#documentation/uikit/reference/UIGestureRecognizer_Class/Reference/Reference.html%23//apple_ref/occ/cl/UIGestureRecognizer

哇,晚了几年......我遇到了这个问题,但用静态变量解决了它:

- (IBAction)handleGesture:(UIPanGestureRecognizer *)recog {

    CGPoint loc = [recognizer locationInView:self.container];
    static CGFloat origin = 0.0f;

    switch (recognizer.state) {
        case UIGestureRecognizerStateBegan:
            origin = loc.x;
            break;
        case UIGestureRecognizerStateChanged:
        case UIGestureRecognizerStatePossible:
            // origin is still set here to the original touch point!
            break;
        case UIGestureRecognizerStateEnded:
            break;
        case UIGestureRecognizerStateFailed:
        case UIGestureRecognizerStateCancelled:
            break;
    }

}

该变量仅在识别器状态为 UIGestureRecognizerBegan 时设置。 由于该变量是静态的,因此该值将保留到以后的方法调用中。

就我而言,我只需要 x 坐标,但如果需要,您可以将其更改为 CGPoint。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM