如何在iOS 7的UIWebView中从JavaScript调用Objective-C?

时间:2014-05-15 18:12:54

标签: ios cordova ios7 uiwebview javascriptcore

请注意,这不是重复的,因为我的问题中的“iOS 7”部分以及之前的行为(因此回答以前的问题)已经改变了。

我想知道通过UIWebView调用的JavaScript代码如何调用iOS 7中的Objective-C代码。这是我以前在项目中广泛使用的东西,但是iOS 7上的行为已经改变,结果是以前没有调用webView委托的didFailLoadWithError。

我曾经使用自定义网址技巧调用Objective-C功能:

function invokeObjectiveC(action, target, data) {
   var iframe = document.createElement("IFRAME");
   iframe.setAttribute("src", "MY-URLScheme:" + action + ":" + target + ":" + data);
   document.documentElement.appendChild(iframe);
   iframe.parentNode.removeChild(iframe);
   iframe = null;
}

导致didFailLoadWithError:被调用的特定行是这一行:

 document.documentElement.appendChild(iframe);

另一种常见的替代方法是在互联网上做同样的事情:

window.location = "MY-URLScheme:" + action + ":" + target + ":" + data;

这适用于iOS 7,因为didFailLoadWithError没有被调用,但是它有一个(已知的)问题,即如果它连续快速调用两次,则第一次调用就会丢失。

所以我想知道Cordova是如何做到这一点的,所以我下载了源代码并看了一下,我他们这样做就像我这样(第3行不同):

var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "MY-URLScheme:" + action + ":" + target + ":" + data);
document.body.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;

然而,这仍会导致调用didFailLoadWithError。这让我很困惑,为什么科尔多瓦没有采取任何措施来纠正这个问题(假设我正在正确地看待他们的代码)。

调用didFailLoadWithError这一事实似乎并不是因为页面仍然加载而且仍然会调用Objective-C。然而,让代码导致调用错误方法是非常非常不令人满意的,所以我想找到另一种方法。

所以问题是:有没有一种方法可以在UIWebView中加载的JS中可靠地调用Objective-C,而不会在iOS 7上调用didFailLoadWithError?

任何人都可以确认Cordova正在使用上述方式或其他方式吗?

在UIWebView中使用JavaScriptCore的解决方案(而不是UIWebView的外部)在iOS / fragile中看起来没有文档/不支持?例如:

Access the JavaScriptCore engine of a UIWebView

Trigger objective C method from javascript using JavaScriptCore in iOS 7 in ViewControllers

1 个答案:

答案 0 :(得分:4)

iframe解决方法有点儿麻烦,不太可靠。 JavaScriptCore是一个很好的方法,但该框架在iOS 6和iOS 5上是私有的。有一种更好,更快的方法从UIWebView调用Objetive-C。您可以为警报功能实现类别方法。

@implementation UIWebView (JavaScriptAlert)
- (void)webView:(UIWebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame 
{
    if ([message hasPrefix:CUSTOM_SCHEME_STRING]) {
        //process your message
    }
    else {
        UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"Alert" message:message delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles: nil];
        [alert show];
        [alert release];
    }
} 
@end

当你从javascript调用window.alert()方法时,会调用上面的Objetive-C函数。当您需要标准警报时,请为您的消息使用自定义前缀并回退到UIAlertView。

此方法适用于iOS 5,6和7,没有任何问题。它非常可靠。我连续测试了10000次调用警报,没有消息丢失。 iframe解决方案的情况并非如此,如果您过快地创建iframe,很多消息都会丢失,因此您已经在JS代码上实现了批处理队列,并以~1ms的间隔同步它们。