10

I have a UIView that has a UIPanGestureRecognizer attached to it the gesture is working fine except that the starting point is not where the pan first started it is usually off by 5 to 15 pixels in both the x and y coordinates.Unfortunately the variance is not consistent and seems to be related to the speed at which the panning motion takes place.

To validate that the touches are being sent correctly I have added a touchesBegan method to a subview and it receives the correct starting point but the gesture does not provide the same point in it's begin phase. Some examples from my logs are below 'Line start point' is the first point received from the gesture recognizer.

touchesBegan got point 617.000000x505.000000
Line start point at 630.000000x504.0000001
touchesBegan got point 403.000000x503.000000
Line start point at 413.000000x504.000000 
touchesBegan got point 323.000000x562.000000
Line start point at 341.000000x568.000000

Has anyone seen this issue before?

Any ideas on how to work around the issue with out having to implement an entirely new UIGestureRecognizer?

Cory Powers
  • 1,140
  • 8
  • 14
  • Does this answer your question? [How do I capture the point initially tapped in a UIPanGestureRecognizer?](https://stackoverflow.com/questions/6950713/how-do-i-capture-the-point-initially-tapped-in-a-uipangesturerecognizer) – Senseful Mar 08 '20 at 22:50

6 Answers6

8

You can detect initial touch position of a gesture with the gesture recognizer's delegate method

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
phru
  • 81
  • 1
  • 2
  • I had the exact problem described by this question, and this helped me solve it the right way (IMHO). CGPoint startingLocation = [touch locationInView:self]; // works inside delegate method – John Fowler Mar 27 '14 at 19:06
  • This works great. Another option using a UILongPressGestureRecognizer and not needing to store the start point. There are pros/cons to both solutions. See [answer here](https://stackoverflow.com/a/60592833/35690) for more details. – Senseful Mar 08 '20 at 22:54
4
  CGPoint beg = [panRecognizer locationInView:_scrollView];
  CGPoint trans = [panRecognizer translationInView:_scrollView];
  CGPoint firstTouch = CGPointSubtract(beg, trans);

Put this code in the UIGestureRecognizerStateBegan case

yuf
  • 3,082
  • 3
  • 20
  • 18
3

Yes the difference is because the gesture recognizer waits a undetermined distance of movement before becoming active. What you can do is create your own UIPanGestureRecognizer and set the state to UIGestureRecognizerStateChanged in the touchesMoved override method.

NOTE: I used touhcesMoved instead of touchesBegan because I wanted it to start when the users touch moved and not instantly.

Here is some example code for a custom gesture recognizer that does this:

#import "RAUIPanGestureRecognizer.h"

@implementation RAUIPanGestureRecognizer 


#pragma mark - UIGestureRecognizerSubclass Methods

- (void)reset
    { [super reset ]; }

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

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
    { 
        [self setState:UIGestureRecognizerStateChanged ];
        [super touchesMoved:touches withEvent:event ]; 
    }

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

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


@end
Designerd
  • 531
  • 5
  • 8
3

The documentation says that the pan gesture starts when the fingers have "moved enough to be considered a pan." This movement is to distinguish a press from a drag, since the user's finger could move around a bit while they are trying to press without dragging.

I think this is the difference you're seeing between the first touch point and the first point considered part of the drag.

Douglas
  • 36,802
  • 9
  • 76
  • 89
  • The problem occurs even if you create your own gesture recognizer by extending UIGestureRecognizer, the starting point is different than the touchesBegan methods. I submitted a bug to apple. – Cory Powers Aug 22 '10 at 16:12
  • Hmm, I'm not sure I understand. If you're implementing your own UIGestureRecognizer, wouldn't you define your own starting point in touchesBegan anyway? – Douglas Aug 22 '10 at 22:03
  • @Douglas - Were you ever to find a solution to this? – Ser Pounce Dec 30 '11 at 15:00
  • @CoDEFRo I've not looked any further than this, perhaps Cory has. – Douglas Dec 30 '11 at 15:11
  • @Cory Powers - any luck solving this? – Ser Pounce Dec 30 '11 at 15:17
  • Apple claimed it is not a bug and works as intended, to get around this "behavior" I also override touchesBegan and store the actual starting point in a ivar to be used by the gesture recognizer method – Cory Powers Feb 26 '12 at 16:56
1

To get around this you could try reseting the translation point when the gesture recognizer begins. For example, start your action method out like so:

- (void)panGesture:(UIPanGestureRecognizer *)recognizer;
{
    if ( recognizer.state == UIGestureRecognizerStateBegan )
    {
        CGPoint point = ...; // The view's initial origin.
        UIView *superview = [recognizer.view superview];
        [recognizer setTranslation:point inView:superview];
    }
}
Marc Charbonneau
  • 40,399
  • 3
  • 75
  • 82
0

The start point changing reason is as @Douglas said:

  • the pan gesture starts when the fingers have moved enough to be considered a pan.

And the starting point and translation are both calculated after the pan gesture is recognized.

I use the following way to get the "real" start point:

For the view which has the pan gesture, override the -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event method, and store the "real" starting point for later use:

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];
    self.touchStartPoint = [[[touches allObjects] firstObject] locationInView:self];
}
HongchaoZhang
  • 3,494
  • 1
  • 17
  • 8