We're building an app that takes advantage of the new UICollectionView in iOS 6. However, we need to implement a long-press behavior such that even if the user then moves their finger after, we want it ignored.
i.e.
User touches the screen than instantly moves -> Swipe
User touches the screen, pauses, then moves -> Ignore swipe and wait for the release.
Basically, we only want to allow the built-in swipe gesture to be enabled if our gesture recognizer fails. However, we're not sure how to supersede the built-in swipe gesture recognizers with that 'Other recognizer must fail first' logic.
We're not sure if we're allowed to simply walk the chain trying to find the UIScrollView and interrogate that as we don't know if that violates Apple's guidelines, and if I remember correctly, they actually warn against messing with their recognizers anyway.
So how can we create tap-to-hold recognizers that supersede the built in ones?
There's an example on page 36 of the UICollectionView programming guide:
UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
NSArray* recognizers = [self.collectionView gestureRecognizers];
// Make the default gesture recognizer wait until the custom one fails.
for (UIGestureRecognizer* aRecognizer in recognizers) {
if ([aRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
[aRecognizer requireGestureRecognizerToFail:tapGesture];
}
}
// Now add the gesture recognizer to the collection view.
tapGesture.numberOfTapsRequired = 2;
[self.collectionView addGestureRecognizer:tapGesture];
Original answer
Have a look at UITapGestureRecognizerDelegate
, which can be used to allow two gesture recognizers to process touches at once:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
For more info, see a tutorial such as this:
http://www.raywenderlich.com/6567/uigesturerecognizer-tutorial-in-ios-5-pinches-pans-and-more
Before your new UILongPressGestureRecognizer
transnitions from the possible state, he will ask its delegate gestureRecognizerShouldBegin:
. You can use that delegate method to cancel (force to failed state) any other gesture recogniser attached to the same view.
You do this by implementing the following as a delegate for your new UILongPressGestureRecognizer
:
@implementation DragPictogramGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
for (UIGestureRecognizer *gr in gestureRecognizer.view.gestureRecognizers) {
if ([gr isKindOfClass:[UILongPressGestureRecognizer class]] == NO) {
gr.enabled = NO;
gr.enabled = YES;
}
}
return YES;
}
@end
Further more, in order to allow the user to use your new UILongPressGestureRecognizer
with one finger, and use another finger to scroll the UICollectionView
at the same time, you can implement the following delegate in the same class.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}