【问题标题】:How to invoke Objective-C method from Javascript and send back data to Javascript in iOS?如何从 Javascript 调用 Objective-C 方法并将数据发送回 iOS 中的 Javascript?
【发布时间】:2012-04-07 06:44:07
【问题描述】:

在 iOS 中,如何在 UIWebView 中从 Javascript 调用 Objective-C 方法并将数据发送回 Javascript?我知道这可以在 OS X 上使用 Webkit 库来完成,但这在 iOS 上可行吗? PhoneGap 是如何做到这一点的?

【问题讨论】:

    标签: javascript ios objective-c uiwebview


    【解决方案1】:

    有一个 API 可以直接从 Objective-C 调用 JavaScript,但是你不能直接从 Javascript 调用 Objective-C。

    如何告诉你的 Objective-C 代码从你的 WebView 中的 Javascript 做一些事情

    您必须将您的 Javascript 操作序列化为一个特殊的 URL,并在 UIWebView 的委托的 shouldStartLoadWithRequest 方法中截取该 URL。

    - (BOOL)webView:(UIWebView *)webView
            shouldStartLoadWithRequest:(NSURLRequest *)request
                        navigationType:(UIWebViewNavigationType)navigationType;
    

    您可以在此处反序列化该特殊 URL 并将其解释为在 Objective-C 端执行您想要的操作。 (您应该在上面的 shouldStartLoadWithRequest 方法中返回 NO,这样 UIWebView 就不会使用您的虚假 URL 来实际发出 HTTP 请求来加载网页。)

    如何从 Objective-C 运行 Javascript 代码

    然后,您可以通过在 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。)

    【讨论】:

    • 很好的例子。从 PHP 你可以这样编码:$fragment = array("key"=>"val"); $url = 'myApp://actionType#'.rawurlencode(json_encode($fragment));
    • 应该 NSString *actionType = request.URL.path;是 NSString *actionType = request.URL.host; ?
    • 我的意思是 "myScheme://myAction#jsonEscapedParameter" URL.host 应该返回 myAction 部分。查看文档developer.apple.com/library/ios/documentation/cocoa/reference/…
    • @Colonize.bat 这种方法被广泛使用,不应该让你拒绝。 (当然,除了死亡和税收之外,生活中没有什么是确定的,但我已经这样做了五年多了,没有任何问题。)
    • ive 只是遇到了一个问题,即 myAppScheme 以小写形式传递给目标 c,尽管使用大写字母发送并检查大写字母。提醒一下
    【解决方案2】:

    更改页面位置会导致多个issues:

    1. 所有 setInterval 和 setTimeout 都会在位置更改时立即停止
    2. 取消位置更改后,每个 innerHTML 都将无法工作!
    3. 您可能会遇到其他非常奇怪的错误,非常难以诊断……

    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;
    

    希望有帮助

    【讨论】:

    【解决方案3】:

    来自 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>
    

    Intercept the URL request

    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-29
      • 2014-03-22
      • 1970-01-01
      • 2011-02-13
      • 2014-05-16
      • 1970-01-01
      • 2015-02-09
      • 1970-01-01
      相关资源
      最近更新 更多