I\'m using JavaScript to detect taps in a page I\'m showing in a UIWebView, like so:
Apparently if you put a click listener on an element, you can no longer select text within that element on iOS. My solution was to detect taps using a combination of touchstart, touchmove, and touchend events, along with a timer to ignore multi-taps, and checking the current document selection to make sure a selection event is not going on.
Here's the JS code I used:
SingleTapDetector = function(element, handler) {
this.element = element;
this.handler = handler;
element.addEventListener('touchstart', this, false);
};
SingleTapDetector.prototype.handleEvent = function(event) {
switch (event.type) {
case 'touchstart': this.onTouchStart(event); break;
case 'touchmove': this.onTouchMove(event); break;
case 'touchend': this.onTouchEnd(event); break;
}
};
SingleTapDetector.prototype.onTouchStart = function(event) {
this.element.addEventListener('touchend', this, false);
document.body.addEventListener('touchmove', this, false);
this.startX = this.currentX = event.touches[0].clientX;
this.startY = this.currentY = event.touches[0].clientY;
this.startTime = new Date().getTime();
};
SingleTapDetector.prototype.onTouchMove = function(event) {
this.currentX = event.touches[0].clientX;
this.currentY = event.touches[0].clientY;
};
SingleTapDetector.prototype.onTouchEnd = function(event) {
var that = this;
// Has there been one or more taps in this sequence already?
if (this.tapTimer) {
// Reset the timer to catch any additional taps in this sequence
clearTimeout(this.tapTimer);
this.tapTimer = setTimeout(function() {
that.tapTimer = null;
}, 300);
} else {
// Make sure the user didn't move too much
if (Math.abs(this.currentX - this.startX) < 4 &&
Math.abs(this.currentY - this.startY) < 4) {
// Make sure this isn't a long press
if (new Date().getTime() - this.startTime <= 300) {
// Make sure this tap wasn't part of a selection event
if (window.getSelection() + '' == '') {
// Make sure this tap is in fact a single tap
this.tapTimer = setTimeout(function() {
that.tapTimer = null;
// This is a single tap
that.handler(event);
}, 300);
}
}
}
}
};
new SingleTapDetector(document.body, function(event) {
document.location = "internal://tap";
});
There is no need to use Javascript for this, it's overkill when the UIGestureRecognizerDelegate has adequate methods. All you need to do is make sure that when text selection is taking place, the tap recogniser isn't triggered.
- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
BOOL hasTap = ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] ||
[otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]);
BOOL hasLongTouch = ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] ||
[otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]);
if (hasTap && hasLongTouch) {
// user is selecting text
return NO;
}
return YES;
}
That takes care of text selection, and links should work fine anyway (at least they do for me).