如何可靠地在UIWebView中检测链接的点击?

9

我有一个 UIWebView,当用户点击链接时,我需要执行某些操作。可以使用委托回调来检测这些点击:

- (BOOL) webView: (UIWebView*) webView
    shouldStartLoadWithRequest: (NSURLRequest*) request
    navigationType: (UIWebViewNavigationType) navigationType
{
    if (navigationType == UIWebViewNavigationTypeLinkClicked) {
        
    }
}

问题在于这段代码无法处理所有的链接点击。例如,普通的 Google 搜索结果页面会对链接进行一些奇怪的处理:
<a href="http://example.com/" class="l" onmousedown="return rwt(…)">
    <em>Link Text</em>
</a>

rwt函数导致链接在被点击时不会触发UIWebViewNavigationTypeLinkClicked事件。有没有一种可靠的方法来检测所有落入“导航到其他页面”类别的事件?

4个回答

6

到目前为止,我已经得出了以下解决方案。首先,在页面加载时,我注入一些JS代码:

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);
}

当用户点击链接时,通过webView:shouldStartLoadWithRequest:navigationType:委托调用,我提前知道这个操作。
if ([[[request URL] scheme] isEqualToString:@"callback"]) {
    [self setNavigationLeavingCurrentPage:YES];
    return NO;
}

如果另一个请求到来,而_navigationLeavingCurrentPage为真,则我知道用户已经点击了一个链接,即使导航类型标志是UIWebViewNavigationTypeOther。 我仍然需要广泛测试解决方案,因为我担心它会导致一些错误的结果。


1
我相信这是可以做到的,通过在网站的HTML代码中嵌入一个自定义JavaScript来监控事件,并根据您想要监视的事件触发一个带有自定义URL方案的页面重定向,您可以在shouldStartLoadWithRequest中拦截它。
类似于这样的东西:
<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>

然后在shouldStartLoadWithRequest中,您可以搜索具有callback:// URL方案的NSURLRequest,并执行您想要的任何操作。

这尚未经过测试,但类似于以下内容可能会让您朝着正确的方向前进。

此外,由于提到了这一点,是的,您可以通过使用以下内容将自定义脚本添加到任何网页:

- (void)webViewDidFinishLoad:(UIWebView *)webView 
{
    [super webViewDidFinishLoad:webView];    
    [webView stringByEvaluatingJavaScriptFromString:@"document.body.insertAdjacentHTML('BeforeEnd','<script>....</script>');"];
}

我认为OP无法控制在UIWebView中显示的网页。因此,他们无法添加自定义JavaScript。 - Black Frog
1
您可以在页面加载后轻松添加自定义脚本。请参阅我的编辑以了解如何执行此操作。 - Lefteris
非常好的教程,介绍了如何将自定义 JavaScript 添加到任何页面。 - Black Frog
我最终也想到了类似的想法(将JavaScript注入页面中),明天会尝试一下。使用自定义URL方案从JavaScript端进行报告是一个很好的技巧! - zoul

1
你需要添加这一行:

webView.dataDetectorTypes = UIDataDetectorTypeAll;

然后,(BOOL) webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType方法将被调用。

0

我在让这个工作起来的过程中遇到了很多问题。使用UITapGestureRecognizer可以解决问题,但是它会接收所有的点击事件,包括链接。不幸的是,链接需要大约300毫秒的时间才能被识别出来,这意味着手势识别器会在链接被识别之前就接收到点击事件,从而创建了一个时间上的问题(更糟糕的是,因为你不应该硬编码等待时间以防万一苹果公司改变了它)。此外,等待300毫秒以上的点击会给我的应用程序带来糟糕的用户体验。

所以我最终做的是在所有东西的顶部覆盖一个透明的div,以获取非链接的点击事件。然后,将链接放在更高的z级别上。最后,确保所有内容都使用ontouchend="javascript:window.location.href='...';"。ontouchend只会在松开手指时触发,这是用户期望的行为。设置window.location.href加载一个新页面,确保所有的点击都调用-webView:shouldStartLoadWithRequest:navigationType:,我可以检查URL来确定我需要做什么。

HTML看起来像:

...
<div ontouchend="javascript:window.location.href='non-link-action';"></div>
...
<a href="link-action">...</a>
...

我不确定 "position: relative" 是否总是将事物放置在与其他情况相同的位置,但它对于我的简单页面效果很好;你的情况可能会有所不同。然而,为了让 z-index 起作用,您需要使用 position:something。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接