How can I reliably detect a link click in UIWebView?

强颜欢笑 提交于 2019-11-29 04:12:54

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.

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>');"];
}

You need to add this line

webView.dataDetectorTypes = UIDataDetectorTypeAll;

then

  • (BOOL) webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType

method will get call.

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.)

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!