请注意,由于我的问题的“在 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/