AlexP
AlexP

Reputation: 1466

Custom UIGestureRecognizer: selector does not receive UIGestureRecognizerStateBegan

I am implementing custom UIGestureRecognizer. For simplicity, let assume that it recognizes gesture that consists of >1 touches.

Here is Gesture.m:

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

#define SHOW printf("%s %d %d %d\n", __FUNCTION__, self.state, touches.count, self.numberOfTouches)

@implementation Gesture

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  SHOW;
  if (self.numberOfTouches==1) return;
  self.state = UIGestureRecognizerStateBegan;
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  SHOW;
  if (self.numberOfTouches==1) return;
  self.state = UIGestureRecognizerStateChanged;
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  SHOW;
  if (self.numberOfTouches==1) return;
  self.state = UIGestureRecognizerStateEnded;
}
@end

Here is a selector:

- (IBAction)handleGesture:(Gesture *)recognizer {
  printf("%s %d\n", __FUNCTION__, recognizer.state);
}

And here is an output:

-[Gesture touchesBegan:withEvent:] 0 1 1  // 1st touch began
-[Gesture touchesMoved:withEvent:] 0 1 1
-[Gesture touchesMoved:withEvent:] 0 1 1
-[Gesture touchesMoved:withEvent:] 0 1 1
-[Gesture touchesBegan:withEvent:] 0 1 2  // 2nd touch began
-[Gesture touchesMoved:withEvent:] 1 1 2  // Gesture.state==UIGestureRecognizerStateBegan but selector was not called
-[ViewController handleGesture:] 2        // UIGestureRecognizerStateChanged received.
-[Gesture touchesMoved:withEvent:] 2 2 2
-[ViewController handleGesture:] 2
-[Gesture touchesMoved:withEvent:] 2 2 2
-[ViewController handleGesture:] 2
-[Gesture touchesMoved:withEvent:] 2 2 2
-[ViewController handleGesture:] 3        // UIGestureRecognizerStateEnded received.

Why doesn't selector receive UIGestureRecognizerStateBegan?

Upvotes: 4

Views: 2637

Answers (1)

Tommy
Tommy

Reputation: 100622

It wasn't particularly obvious to me but the rules seem to be:

  • all touches sent to you via touchesBegan:... are subsequently considered to belong to you unless and until you set the state to UIGestureRecognizerStateFailed, UIGestureRecognizerStateEnded or UIGestureRecognizerStateCancelled;
  • if instead you transition to UIGestureRecognizerStateBegan then the gesture recogniser will post that on, then automatically generate UIGestureRecognizerStateChanged when the touches that are now assigned to you move.

So you're not supposed to set UIGestureRecognizerStateChanged for yourself — just keep track of the touches and correctly post began, ended, failed and cancelled.

In your case I think you just need to remove the state set within touchesMoved:....

(aside: the above is true of iOS 5 and 6; under 4 the behaviour is slightly more subtle. To work under all three versions use a construction like if(self.state == UIGestureRecognizerStateBegan) self.state = UIGestureRecognizerStateChanged; when you know that your properties have changed)

Upvotes: 3

Related Questions