Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

allowableMovement seems to be ignored

The allowableMovement property in my UILongPressGestureRecognizer seems to be ignored. I created a new project (Xcode 4.5.1, iOS 6) using the Single View Application template and added a Long Press Gesture Recognizer to the view. There's an outlet wired up and an action. Here's the code for the action method:

- (IBAction)longPress:(UILongPressGestureRecognizer *)sender
{    
    if (sender.state == UIGestureRecognizerStatePossible)   NSLog(@"possible");
    if (sender.state == UIGestureRecognizerStateBegan)      NSLog(@"began");
    if (sender.state == UIGestureRecognizerStateChanged)    NSLog(@"changed");    
    if (sender.state == UIGestureRecognizerStateRecognized) NSLog(@"recognized");    
    if (sender.state == UIGestureRecognizerStateCancelled)  NSLog(@"cancelled");
    if (sender.state == UIGestureRecognizerStateFailed)     NSLog(@"failed");

    CGPoint locationInView = [sender locationInView:self.view];

    NSLog(@"long press: allowableMovement= %f, x= %f, y= %f", sender.allowableMovement, locationInView.x, locationInView.y);
}

If I press long enough and let go I get this in the log:

2012-10-30 20:24:41.449 Long Press[1078:907] began
2012-10-30 20:24:41.455 Long Press[1078:907] long press: allowableMovement= 10.000000, x= 210.500000, y= 99.500000
2012-10-30 20:24:42.880 Long Press[1078:907] recognized
2012-10-30 20:24:42.882 Long Press[1078:907] long press: allowableMovement= 10.000000, x= 208.500000, y= 96.000000

This is what I would expect.

But no matter what I set allowableMovement to (positive, negative, big, small), once the state is UIGestureRecognizerStateBegan, I can drag my finger all over the screen. The state changes to UIGestureRecognizerStateChanged and there are frequent updates and locationInView continues to accurately track. When I let go I get the UIGestureRecognizerStateRecognized state and a final output to the log.

The class reference says that the recognizer should fail if movement exceeds allowableMovement. Why does the allowableMovement property seem to be ignored?

like image 620
Murray Sagal Avatar asked Oct 30 '12 19:10

Murray Sagal


2 Answers

The allowableMovement property is not about how far you can drag after the gesture has started. It is about how far you can move before the gesture starts.

From the class reference:

The gesture begins (UIGestureRecognizerStateBegan) when the number of allowable fingers (numberOfTouchesRequired) have been pressed for the specified period (minimumPressDuration) and the touches do not move beyond the allowable range of movement (allowableMovement).

This becomes apparent when you set minimumPressDuration to something high like 3 seconds and allowableMovement to something low like 1 pixel. If your finger rolls at all the gesture won't begin. But if you set allowableMovement to 100 your finger can roll around quite a bit and the gesture will start.

In this way it is like the other properties. They are all about what is required for the gesture to start.

like image 193
Murray Sagal Avatar answered Sep 19 '22 00:09

Murray Sagal


I thought allowableMovement had exactly that purpose. I made a small subclass to implement the "expected" behavior with a allowableMovementAfterBegan.

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

@interface TDMLongPressGestureRecognizer : UILongPressGestureRecognizer
@property (nonatomic, assign) CGFloat allowableMovementAfterBegan;
@end

@implementation TDMLongPressGestureRecognizer
{
    CGPoint initialPoint;
}

- (instancetype)initWithTarget:(id)target action:(SEL)action
{
    self = [super initWithTarget:target action:action];
    if (self) {
        // Use default value for allowableMovement before touches begin
        _allowableMovementAfterBegan = self.allowableMovement; 
    }
    return self;
}

- (void)reset
{
    [super reset];      
    initialPoint = CGPointZero;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];   
    initialPoint = [self locationInView:self.view];
}

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

    if (!CGPointEqualToPoint(initialPoint, CGPointZero))
    {
        CGPoint currentPoint = [self locationInView:self.view];

        CGFloat distance = hypot(initialPoint.x - currentPoint.x, initialPoint.y - currentPoint.y);
        if (distance > self.allowableMovementAfterBegan)
            self.state = UIGestureRecognizerStateFailed;
        }
    }
}
like image 40
tobiasdm Avatar answered Sep 21 '22 00:09

tobiasdm