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:
- touch happens
- gesture intercepts
- gesture is recognized
touchesBegan
actually happened (because the gesture recognized)
- no delay,
touchesBegan
fires
With 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:
- gesture is being recognized
- gesture completes successfully
- gesture CANCELS touches for the view in which the gesture happens (this is expected behaviour of
UIGestureRecognizer
... touchesEnded
happens when:
- gesture is being recognized
- gesture completes UNSUCCESSFULLY
touchesEnded
is fired
With 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.
A different approach
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.
Bottom Line
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.