Reputation: 104065
I have a UIWebView
and I need to do something when user taps a link. There’s a delegate callback that can be used to detect the taps:
- (BOOL) webView: (UIWebView*) webView
shouldStartLoadWithRequest: (NSURLRequest*) request
navigationType: (UIWebViewNavigationType) navigationType
{
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
…
}
}
The problem is that this code doesn’t handle all link clicks. As an example, a plain Google Search results page does something weird with the links:
<a href="http://example.com/" class="l" onmousedown="return rwt(…)">
<em>Link Text</em>
</a>
The rwt
function results in the links not triggering the UIWebViewNavigationTypeLinkClicked
event when tapped. Is there a way to reliably detect all events that fall into the “navigate to some other page” bucket?
Upvotes: 9
Views: 15441
Reputation: 629
You need to add this line
webView.dataDetectorTypes = UIDataDetectorTypeAll;
then
- (BOOL) webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType
method will get call.
Upvotes: 1
Reputation: 1647
I had a bunch of problems getting this to work. Using a UITapGestureRecognizer works, except that it will always receive the taps, even for links. Unfortunately, links have about a 300 ms delay before they get recognized, which means that the gesture recognizer gets the taps before the link is recognized, which creates a timing problem (even worse because you shouldn't hard code a time to wait in case Apple changes it). Plus, waiting 300+ ms for the tap gives a poor user experience for my app.
So what I ended up doing was overlaying a transparent div on top of everything that gets the non-link taps. Then, put the links at a higher z-level. Finally, make sure everything uses ontouchend="javascript:window.location.href='...';"
. ontouchend will only act on a tap release, which is what users expect. Setting window.location.href loads a new page, ensuring that all taps call -webView:shouldStartLoadWithRequest:navigationType:
and I can check the URL to see which I need to do.
The HTML looks like: ... ... ... ...
(I'm not totally sure if "position: relative" always places things in the same position as otherwise, but it worked nicely for my simple page; your mileage may vary. However, you will needs position:something in order to get z-index to work.)
Upvotes: 0
Reputation: 104065
So far I have arrived at the following solution. First, I inject some JS code into the page when loaded:
function reportBackToObjectiveC(string)
{
var iframe = document.createElement("iframe");
iframe.setAttribute("src", "callback://" + string);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
}
var links = document.getElementsByTagName("a");
for (var i=0; i<links.length; i++) {
links[i].addEventListener("click", function() {
reportBackToObjectiveC("link-clicked");
}, true);
}
When user taps a link, I know it in advance thanks to the webView:shouldStartLoadWithRequest: navigationType:
delegate call:
if ([[[request URL] scheme] isEqualToString:@"callback"]) {
[self setNavigationLeavingCurrentPage:YES];
return NO;
}
Then if another request comes and _navigationLeavingCurrentPage
is true, I know the user has clicked a link even though the navigation type flag is UIWebViewNavigationTypeOther
. I still have to test the solution extensively, for I’m afraid that it will lead to some false positives.
Upvotes: 6
Reputation: 14677
I believe this can be done, by embedding in the HTML code of the website a custom JavaScript which will monitor the events, and based on which event you want to monitor, it could trigger a page redirect with a custom URL scheme, which you can intercept in the shouldStartLoadWithRequest.
Something like this:
<script>
// Function to capture events
function captureEvent(el) {
window.location.href="callback://"+el.href;
}
var elms = document.getElementsByTagName("a");
for (var i=0; i<elms.length; i++) {
elms[i].addEventListener("onmousedown", function(){captureEvent(el)}, true);
}
</script>
Then in the shouldStartLoadWithRequest, you can search for NSURLRequest's that have a callback:// url scheme, and do whatever you want.
This has not been tested, but something like this might get you in the right direction.
Also, since this was mentioned, yes you can add your custom script to any webpage, by using this:
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[super webViewDidFinishLoad:webView];
[webView stringByEvaluatingJavaScriptFromString:@"document.body.insertAdjacentHTML('BeforeEnd','<script>....</script>');"];
}
Upvotes: 1