25

我有一个应用程序可以让用户在屏幕上跟踪线条。我通过在 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 时再参考它。不过,我想我会在这里问,然后再走那条路。

4

8 回答 8

25

聚会迟到了,但我注意到上面没有任何东西能真正回答这个问题,实际上有一种方法可以做到这一点。您必须继承 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,这是有道理的。您可能希望使用更复杂的方法来查找感兴趣的触摸。

编辑:斯威夫特 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)
  }
}
于 2015-07-04T21:04:13.033 回答
21

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

于 2011-08-05T02:35:04.157 回答
9

@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)")
于 2017-05-11T21:01:54.553 回答
5

你可以使用这个方法:

CGPoint point    = [gesture locationInView:self.view];
于 2013-06-17T21:47:52.290 回答
1

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

于 2014-04-17T01:32:37.353 回答
0

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

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

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

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

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


解决方案一: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!)
}
于 2020-03-08T22:52:29.963 回答
-2

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

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

于 2011-08-05T02:34:41.427 回答
-3

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

- (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。

于 2016-12-07T04:35:46.197 回答