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

如何在iOS 7的UIWebView中从JavaScript调用Objective-C?,ios,cordova,ios7,uiwebview,javascriptcore,Ios,Cordova,Ios7,Uiwebview,Javascriptcore,注意,由于我的问题中的“在iOS 7中”部分以及之前的行为(以及之前问题的答案)已经改变,因此这不是重复 我想知道通过UIWebView调用的JavaScript代码如何在iOS 7中调用Objective-C代码。这是我以前在项目中广泛使用的东西,但是iOS 7上的行为发生了变化,结果webView委托的didFailLoadWithError在以前没有被调用时被调用 我曾经使用自定义url技巧调用Objective-C功能: function invokeObjectiveC(action,

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

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

我曾经使用自定义url技巧调用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;
}
导致调用DidFailLoadRor:的特定行是:

 document.documentElement.appendChild(iframe);
另一种常见的替代方法是在互联网上做同样的事情:

window.location = "MY-URLScheme:" + action + ":" + target + ":" + data;
这在iOS 7上起作用,因为didFailLoadWithError没有被调用,但是它有一个(已知的)问题,如果它连续两次被调用,第一次调用就会丢失

所以我想知道Cordova是如何做到这一点的,所以我下载了源代码并查看了一下,我认为他们的做法与我类似(第三行不同):

然而,这仍然会导致Didror被调用。这让我感到困惑,为什么科尔多瓦没有做任何事情来纠正这一点(假设我正确地查看了他们的代码)

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

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

有人能确认科尔多瓦是在用上述方式还是其他方式吗

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


iframe解决方案有点问题,不太可靠。JavaScriptCore是一种很好的方法,但该框架在iOS 6和iOS 5上是私有的。有一种更好更快的方法可以从UIWebView调用Objective-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()方法时,将调用上述Objective-C函数。为消息使用自定义前缀,并在需要标准警报时回退到UIAlertView


此方法在iOS 5、6和7上运行,没有问题。它非常可靠。我已经连续10000次测试调用警报,没有消息丢失。iframe解决方案并非如此,如果您创建iframe太快,许多消息会丢失,因此您必须在JS代码上实现一个批处理队列,并以~1ms的间隔对其进行同步。

iframe解决方案有点问题,不太可靠。JavaScriptCore是一种很好的方法,但该框架在iOS 6和iOS 5上是私有的。有一种更好更快的方法可以从UIWebView调用Objective-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()方法时,将调用上述Objective-C函数。为消息使用自定义前缀,并在需要标准警报时回退到UIAlertView


此方法在iOS 5、6和7上运行,没有问题。它非常可靠。我已经连续10000次测试调用警报,没有消息丢失。iframe解决方案并非如此,如果创建iframe太快,许多消息会丢失,因此您必须在JS代码上实现一个批处理队列,并以~1ms的间隔对其进行同步。

谢谢,我会尝试一下。如果您只在iOS 7上运行,是否有JavaScriptCore解决方案?@MortimerGoro谢谢..我想知道WebFrame参数是什么意思,谢谢advanced@MacGeekWebFrame参数存在,因为它位于runJavaScriptAlertPanelWithMessage方法的签名中。您不必在方法实现中使用它,只需忽略它。谢谢@MortimerGoro,我会尝试一下:)谢谢,我会尝试一下。如果您只在iOS 7上运行,是否有JavaScriptCore解决方案?@MortimerGoro谢谢..我想知道WebFrame参数是什么意思,谢谢advanced@MacGeekWebFrame参数存在,因为它位于runJavaScriptAlertPanelWithMessage方法的签名中。您不必在方法实现中使用它,只需忽略它即可。谢谢@MortimerGoro,我将尝试使用它:)