I am trying to get touchesEnded to fire after a gesture. I've included a code snippet where both touchesBegan and touchesEnded are enabled to fire. I get two odd behaviours with the code below. When I pan it prints begin and swiped ended. When I touch the canvas I get a begin and two end messages.
I've tried removing the touchesBegan to be sure it doesn't conflict with touchesEnded but the behaviour is the same.
#import "C4WorkSpace.h"
@implementation C4WorkSpace
-(void)setup
{
[self addGesture:PAN name:@"pan" action:@"bbb:"];
[self gestureForName:@"pan"].delaysTouchesBegan = NO;
[self gestureForName:@"pan"].delaysTouchesEnded = NO;
}
-(void) bbb : (UIGestureRecognizer *) recognizer
{
if(recognizer.state == UIGestureRecognizerStateEnded)
C4Log(@"Swipe Ended");
}
-(void) touchesBegan
{
C4Log(@"Begin");
}
-(void) touchesEnded
{
C4Log(@"End");
}
@end
This is an issue that deals directly with the way that UIGestures are architected to work. The delaysTouchesEnded
basically delays the sending of a message to touchesEnded:withEvent:
and setting this value to NO
doesn't necessarily mean that the event will be triggered.
From the docs:
If the gesture recognizer subsequently recognizes its gesture, these touch objects are cancelled (via a touchesCancelled:withEvent: message). If the gesture recognizer does not recognize its gesture, the window delivers these objects in an invocation of the view’s touchesEnded:withEvent: method. Set this property to NO to have touch objects in the UITouchPhaseEnded delivered to the view while the gesture recognizer is analyzing the same touches.
In your code snippet the touchesBegan
and touchesEnded
are NOT actually enabled to fire. What is happening is you're disabling the "delay" on whether or not the gesture will allow the touchesBegan
or touchesEnded
to be available for firing.
With delaysTouchesBegan
set to NO, the following happens:
touchesBegan
actually happened (because the gesture recognized) touchesBegan
firesWith delaysTouchesEnded
the firing of touchesEnded
is contingent on whether or not the gesture successfully completes... Unlike the previous case where touchesBegan
always actually happens at the beginning of the gesture.
What happens in this case is the following:
UIGestureRecognizer
... touchesEnded
happens when:
touchesEnded
is firedWith your code if you touch down, hold the gesture without moving your finger, and then release after a little while the touchesEnded
gets fired. The reason is that the PAN
doesn't complete successfully and allows the touchesEnded
to fire.
You are working with a gesture, so any interaction you want to have happen should take into consideration the gesture you're working with... That is, when you start working with a gesture try and think in terms of the gesture, knowing that it is going to come between the view you're touching and its inherent touchesBegan
, etc., methods.
Your bbb:
method is perfect.
When working with gestures, pipe through a method like this to determine the various states of the gesture. This is how you want to work with gestures.
Try the following code:
#import "C4WorkSpace.h"
@implementation C4WorkSpace
-(void)setup
{
[self addGesture:PAN name:@"pan" action:@"bbb:"];
[self gestureForName:@"pan"].delaysTouchesBegan = NO;
[self gestureForName:@"pan"].delaysTouchesEnded = NO;
}
-(void) bbb : (UIGestureRecognizer *) recognizer {
if(recognizer.state == UIGestureRecognizerStateBegan) {
C4Log(@"PAN Begin");
}
if(recognizer.state == UIGestureRecognizerStateEnded) {
C4Log(@"PAN ended");
}
}
-(void)touchesBegan {
C4Log(@"A touch began");
}
-(void)touchesEnded {
C4Log(@"A touch ended");
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
C4Log(@"A touch cancelled");
}
@end
Notice that the TOUCH
event gets cancelled after you BEGIN
the gesture? This is why touchesEnded
will never be fired, because when the GESTURE begins the system recognizes that the "touch" isn't really a touch and is really a gesture.