I know this question is old, but to keep it current for future searchers, I figured I would add another solution that has worked for me from iOS 7 through 10. It basically brings together the solutions discussed here and here but tweaks them to get the UITextView
to recognize the custom double tap.
It does this by subclassing the UITextView
and overriding the addGestureRecognizer:
method in order to inject our custom callback into the double-tap gesture and configure the single-tap gesture to respect the new double tap hook.
I do this in addGestureRecognizer:
because a UITextView
constantly deletes and adds gestures depending on its current state and so you constantly have to reset it back up.
This code should be enough to get someone started:
@interface MyCustomTextView ()
/**
* we want to keep track of the current single-tap gesture so we can make sure
* it waits for a double-tap gesture to fail before firing
*/
@property (weak, nonatomic) UITapGestureRecognizer *singleTap;
/**
* we want to keep track of the current double-tap gesture so we can tell a single
* tap gesture to ignore this double-tap when the single tap gesture changes
*/
@property (weak, nonatomic) UITapGestureRecognizer *doubleTap;
@end
@implementation MyCustomTextView
/**
* this will fire when the text view is double-tapped
*
* @param tgr
*/
- (void)_handleTwoTaps:(UITapGestureRecognizer *)tgr
{
// ADD CODE HERE
}
/**
* the reason why I've overridden this methods is these gestures change quite a bit
* depending on the state of the UITextView, (eg, when in focus and out of focus)
* and so this provides a reliable way to make sure any new gestures get updated
* with custom overrides.
*
* @param gestureRecognizer
*/
- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
[super addGestureRecognizer:gestureRecognizer];
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
UITapGestureRecognizer *tgr = (UITapGestureRecognizer *)gestureRecognizer;
if ([tgr numberOfTapsRequired] == 1 && [tgr numberOfTouchesRequired] == 1) {
self.singleTap = tgr;
if (self.doubleTap) {
[tgr requireGestureRecognizerToFail:self.doubleTap];
}
} else if ([tgr numberOfTapsRequired] == 2 && [tgr numberOfTouchesRequired] == 1) {
[tgr addTarget:self action:@selector(_handleTwoTaps:)];
self.doubleTap = tgr;
if (self.singleTap) {
[self.singleTap requireGestureRecognizerToFail:tgr];
}
}
}
}
// NOTE: I'm not sure if this is needed but it's been there for years
// and so I thought I would include it just in case
- (void)removeGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
UITapGestureRecognizer *tgr = (UITapGestureRecognizer *)gestureRecognizer;
if ([tgr numberOfTapsRequired] == 2 && [tgr numberOfTouchesRequired] == 1) {
[tgr removeTarget:self action:@selector(_handleTwoTaps:)];
}
}
[super removeGestureRecognizer:gestureRecognizer];
}
@end