如何在 iOS 7 的 UIWebView 中从 JavaScript 调用 Objective-C?
Posted
技术标签:
【中文标题】如何在 iOS 7 的 UIWebView 中从 JavaScript 调用 Objective-C?【英文标题】:How to invoke Objective-C from JavaScript within UIWebView in iOS 7? 【发布时间】:2014-05-15 18:12:54 【问题描述】:请注意,这不是重复的,因为我的问题的“in 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;
导致 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
【问题讨论】:
【参考方案1】: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 代码上实现了一个批处理队列,并以大约 1 毫秒的间隔同步它们。
【讨论】:
谢谢,我试试。如果您只在 iOS 7 上运行,是否有 JavaScriptCore 解决方案? @MortimerGoro 谢谢..我想知道您所说的 WebFrame 参数是什么意思,非常感谢 @MacGeek WebFrame 参数存在,因为它在 runJavaScriptAlertPanelWithMessage 方法的签名中。您不必在方法实现中使用它,只需忽略它即可。 谢谢@MortimerGoro 我会尝试的:)以上是关于如何在 iOS 7 的 UIWebView 中从 JavaScript 调用 Objective-C?的主要内容,如果未能解决你的问题,请参考以下文章
IOS6 横向在仅纵向 iPhone 应用程序中从 uiwebview 播放嵌入的 youtube 视频