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
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.
You can detect initial touch position of a gesture with the gesture recognizer's delegate method
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
The start point changing reason is as @Douglas said:
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];
}
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
CGPoint beg = [panRecognizer locationInView:_scrollView];
CGPoint trans = [panRecognizer translationInView:_scrollView];
CGPoint firstTouch = CGPointSubtract(beg, trans);
Put this code in the UIGestureRecognizerStateBegan case
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];
}
}