15

My view has a UIPanGestureRecognizer added to it.
The problem is that by the time the touch is recognized as a pan gesture, it has to be moved a few points, and I can't extract the original touch location at the UIGestureRecognizerStateBegan state. In this state the translationInView: is (0,0), and any subsequent moves are calculated from this point, and not from the original.

Is there a way to extract the original location from the gesture recognizer itself, or do I need to override the touchesBegan: method?

antalkerekes
  • 2,128
  • 1
  • 20
  • 36

4 Answers4

9

You should be able to set the delegate for the gesture recogniser and implement

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch

to get the initial touch.

Sherman Lo
  • 2,759
  • 20
  • 20
  • 2
    This works, too. However, at the end I used `touchesBegan:`. It seems, the pan gesture recognizer really does not have a clue where the touch event originally started. – antalkerekes Sep 21 '11 at 23:39
3

You can solve this problem by implementing a custom PanGestureRecognizer which saves the original touch point and makes it available to the caller.

I went this route as I also wanted to control the distance moved to trigger a pan gesture.

This might be overkill for your situation, but works perfectly and is less difficult than it sounds.

To get the touchpoint coordinates, you just call:

[sender touchPointInView:yourView] 

instead of:

[sender locationInView:yourView]

Here's the code for PanGestureRecognizer.m:

#import "PanGestureRecognizer.h"
#import <UIKit/UIGestureRecognizerSubclass.h>

@implementation PanGestureRecognizer

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];
    UITouch *touch = [touches anyObject];

    // touchPoint is defined as: @property (assign,nonatomic) CGPoint touchPoint;

    self.touchPoint = [touch locationInView:nil];   
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesMoved:touches withEvent:event];
    UITouch *touch = [touches anyObject];
    CGPoint p = [touch locationInView:nil];

    // customize the pan distance threshold

    CGFloat dx = fabs(p.x-self.touchPoint.x);
    CGFloat dy = fabs(p.y-self.touchPoint.y);

    if ( dx > 2 || dy > 2)  {
        if (self.state == UIGestureRecognizerStatePossible) {
            [self setState:UIGestureRecognizerStateBegan];
        }
        else {
            [self setState:UIGestureRecognizerStateChanged];
        }
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];

    if (self.state == UIGestureRecognizerStateChanged) {
        [self setState:UIGestureRecognizerStateEnded];
    }
    else {
        [self setState:UIGestureRecognizerStateCancelled];
    }
}

- (void) reset
{
}

// this returns the original touch point

- (CGPoint) touchPointInView:(UIView *)view
{
    CGPoint p = [view convertPoint:self.touchPoint fromView:nil];

    return p;
}

@end
Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
northernman
  • 1,446
  • 16
  • 19
0

If you use locationInView: rather than translationInView: you will get absolute coordinates instead of relative coordinates. However you do have to start the pan to get input... To work around this your view can be a UIButton and you can trigger a - (IBAction)buttonWasTouched:(UIButton *)button forEvent:(UIEvent *)event connected to "touch down" like so:

-(IBAction)shakeIntensityPanGesturebuttonWasTouched:(UIButton *)button forEvent:(UIEvent *)event
{
    UITouch *touch = [[event allTouches] anyObject];
    CGPoint location = [touch locationInView:touch.view]; //touch location local cordinates
}
Andres Canella
  • 3,706
  • 1
  • 35
  • 47
-1

You can use the UIGestureRecognizerState

- (void) onPanGesture:(UIPanGestureRecognizer *)sender {
    if(sender.state == UIGestureRecognizerStateBegan) {
        origin = [sender locationInView];
    } else {
        current = [sender locationInView];
        // Initial touch stored in origin
    }
}

Swift (ish):

var state = recognizer.state
if state == UIGestureRecognizerState.Began {
    println(recognizer.locationInView(viewForGesture))
    //this will be the point of first touch
}
Joakim
  • 3,224
  • 3
  • 29
  • 53
Aiden
  • 9
  • 5
  • Gesture recognizers have different states (.Began, .Ended, .Cancelled, etc...). If you get the location of the (view or touch) when the recognizer state is .Began you will have its first location. – Aiden Oct 21 '15 at 15:36
  • Place this code inside the function that gets called by the gesture. The println line above will only print one coordinate pair per touch/pan – Aiden Oct 21 '15 at 15:37
  • 2
    Overriding `touchesBegan` and printing its value demonstrates it is not the same as the value returned by `locationInView` when the state is `.Began`. – par Jan 06 '16 at 06:08