【发布时间】:2012-04-07 06:44:07
【问题描述】:
在 iOS 中,如何在 UIWebView 中从 Javascript 调用 Objective-C 方法并将数据发送回 Javascript?我知道这可以在 OS X 上使用 Webkit 库来完成,但这在 iOS 上可行吗? PhoneGap 是如何做到这一点的?
【问题讨论】:
标签: javascript ios objective-c uiwebview
在 iOS 中,如何在 UIWebView 中从 Javascript 调用 Objective-C 方法并将数据发送回 Javascript?我知道这可以在 OS X 上使用 Webkit 库来完成,但这在 iOS 上可行吗? PhoneGap 是如何做到这一点的?
【问题讨论】:
标签: javascript ios objective-c uiwebview
有一个 API 可以直接从 Objective-C 调用 JavaScript,但是你不能直接从 Javascript 调用 Objective-C。
您必须将您的 Javascript 操作序列化为一个特殊的 URL,并在 UIWebView 的委托的 shouldStartLoadWithRequest 方法中截取该 URL。
- (BOOL)webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType;
您可以在此处反序列化该特殊 URL 并将其解释为在 Objective-C 端执行您想要的操作。 (您应该在上面的 shouldStartLoadWithRequest 方法中返回 NO,这样 UIWebView 就不会使用您的虚假 URL 来实际发出 HTTP 请求来加载网页。)
然后,您可以通过在 webview 上调用此方法从 Objective-C 运行 Javascript。
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
我建议使用虚假的 URL 方案,以便轻松区分您的操作 URL 和合法请求。您可以按照以下方式在 Javascript 中提出此请求:
// JavaScript to send an action to your Objective-C code
var myAppName = 'myfakeappname';
var myActionType = 'myJavascriptActionType';
var myActionParameters = {}; // put extra info into a dict if you need it
// (separating the actionType from parameters makes it easier to parse in ObjC.)
var jsonString = (JSON.stringify(myActionParameters));
var escapedJsonParameters = escape(jsonString);
var url = myAppName + '://' + myActionType + "#" + escapedJsonParameters;
document.location.href = url;
然后在UIWebView.delegate 的shouldStartLoadWithRequest 方法中,您可以检查URL 方案和片段以检查它是正常请求还是您的特殊操作之一。 (URL 的片段是在# 之后。)
- (BOOL)webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
// these need to match the values defined in your JavaScript
NSString *myAppScheme = @"myfakeappname";
NSString *myActionType = @"myJavascriptActionType";
// ignore legit webview requests so they load normally
if (![request.URL.scheme isEqualToString:myAppScheme]) {
return YES;
}
// get the action from the path
NSString *actionType = request.URL.host;
// deserialize the request JSON
NSString *jsonDictString = [request.URL.fragment stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
// look at the actionType and do whatever you want here
if ([actionType isEqualToString:myActionType]) {
// do something in response to your javascript action
// if you used an action parameters dict, deserialize and inspect it here
}
// make sure to return NO so that your webview doesn't try to load your made-up URL
return NO;
}
(如果您需要帮助将 json 字符串反序列化为 NSDictionary,请阅读 this answer。)
【讨论】:
$fragment = array("key"=>"val"); $url = 'myApp://actionType#'.rawurlencode(json_encode($fragment));
更改页面位置会导致多个issues:
here 提供的解决方案是:
继续使用相同的 URL 技术(在 js 和 Objective-c 上),只需使用 iframe 更改位置:
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "js-frame:myObjectiveCFunction";
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
希望有帮助
【讨论】:
来自 Objective-C:您可以将字符串中的 javascript 函数传递给 UIWebView。网页将执行它并返回结果。通过这种方式,您可以传递变量并从 Javascript 中获取数据。
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script
例子:
NSString *script = @"document.getElementById('myTextField').value";
NSString *output = [myWebView stringByEvaluatingJavaScriptFromString:script];
来自 Javascript:在 URL 中传递您的数据。在 UIWebViewDelegate 中拦截 URL 请求。返回 NO 获取数据并中止 URL 请求。
<script>window.location = "url://key1/value1"; </script>
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
【讨论】: