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

标签 ios cordova ios7 uiwebview javascriptcore

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

我想知道通过 UIWebView 调用的 JavaScript 代码如何在 iOS 7 中调用 Objective-C 代码。这是我过去在项目中广泛使用的东西,但是 iOS 7 上的行为发生了变化,结果是webView 委托(delegate)的 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;
}

导致 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 被调用。这让我感到困惑,为什么 Cordova 没有做任何事情来纠正这个问题(假设我正确地查看了他们的代码)。

didFailLoadWithError 被调用的事实似乎并不有害,因为页面仍在加载并且 Objective-C 仍然被调用。但是,拥有导致调用错误方法的代码非常令人不满意,因此我想找到一种替代方法。

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

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

在 UIWebView(相对于 UIWebView 外部)中使用 JavaScriptCore 的解决方案在 iOS 中看起来没有记录/不受支持/脆弱?例如:

Access the JavaScriptCore engine of a UIWebView

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

最佳答案

iframe 解决方法有点问题,而且不是很可靠。 JavaScriptCore 是一种很好的方法,但该框架在 iOS 6 和 iOS 5 上是私有(private)的。有一种更好更快的方法可以从 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 代码上实现了一个批处理队列,并以大约 1 毫秒的间隔同步它们。

关于ios - 如何在 iOS 7 的 UIWebView 中从 JavaScript 调用 Objective-C?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23685735/

相关文章:

iOS - subview 不使用帧更改进行动画处理

ios - 删除 TabGroup 选项卡标题并仅在适用于 iOS 的 Appcelerator 应用程序上显示图标

ios - 如何限制捏缩放图像向图像边界的移动?

objective-c - GAIUncaughtExceptionHandler 仅在 iOS 7 下崩溃

ios7 - 在 ios7 模拟器上使用美元符号 ($) 作为密码

ios - 如何将 dSYM 上传/下载到 App Store Connect (Testflight)?

javascript - 在 PalmOs 中插入更新和删除操作的简单代码?

javascript - 如何向用户显示 ERR_NAME_NOT_RESOLVED 错误

android - 如何更改应用程序显示名称 phonegap

ios - 如何保存屏幕控制?