I have a view to which I've added both a pan and long press UIGestureRecognizer. The pan is used to move the view around. What I'd like to do is notice also that the touch has stopped moving (while remaining active) and trigger the long press.
What I find is that the long press is never triggered after the pan has begun. I've tried setting a delegate and implementing:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
NSLog(@"simultaneous %@", gestureRecognizer.class);
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
NSLog(@"require fail %@", gestureRecognizer.class);
return [gestureRecognizer isKindOfClass:[UIPanGestureRecognizer self]];
// also tried return YES;
// also tried return [gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer self]];
}
I've tried fooling with the pan gr's allowableMovement, also to no avail. I'm just about to give up and use a timer in the pan gr that get's invalidated and then reset on moves, but I was hoping that the SDK would do the state machine stuff for me.
In case anyone else needs it, here's the code that works for me. The goal is to have a view that is sensitive to both long press and pan, including a long press that isn't preceded by a pan and vice versa.
// setup
@property (strong,nonatomic) NSTimer *timer; // triggers the long press during pan
@property (strong,nonatomic) UIView *longPressView; // need this to track long press state
// view is the view we're interested in panning and long pressing
UIPanGestureRecognizer *panGR = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGR:)];
[view addGestureRecognizer:panGR];
// this starts a long press when no pan has occurred
UILongPressGestureRecognizer *longGR = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressGR:)];
[view addGestureRecognizer:longGR];
When a pan begins or changes, start a timer. If the timer expires before the pan ends (touch releases), then we have a long press.
- (void)panGR:(UIPanGestureRecognizer *)gr {
if (gr.state == UIGestureRecognizerStateBegan) {
[self startTimer:gr.view];
} else if (gr.state == UIGestureRecognizerStateChanged) {
[self startTimer:gr.view];
// do whatever you want to do with pan state in this method
// in my case, I'm translating the view here
} else if (gr.state == UIGestureRecognizerStateEnded) {
if (self.longPressView) {
[self longPressEnded];
} else {
[self.timer invalidate];
}
}
}
We give the timer user info of the view. You might need to store other parts of the gesture state, like location, etc. Do it the same way, with the user info dictionary.
- (void)startTimer:(UIView *)view {
if (self.longPressView) return;
[self.timer invalidate];
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.8 target:self
selector:@selector(longPressTimer:)
userInfo:@{ @"view": view} repeats:NO];
}
-(void)longPressTimer:(NSTimer *)timer {
self.longPressView = timer.userInfo[@"view"];
[self longPressBegan];
}
Since the timer method won't have an associated gr, factor out all of the logic that we'd normally put in the gr handler so it can be called by both the timer handler and the gr handler.
- (void)longPressGR:(UILongPressGestureRecognizer *)gr {
if (gr.state == UIGestureRecognizerStateBegan) {
self.longPressView = gr.view;
[self longPressBegan];
} else if (gr.state == UIGestureRecognizerStateEnded) {
[self longPressEnded];
}
}
- (void)longPressBegan {
NSLog(@"long press began");
}
- (void)longPressEnded {
self.longPressView = nil;
NSLog(@"long press ended");
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With