3

我一直在使用WebViewJavascriptBridge在 iOS 应用程序中连接 Objective-C 和 JavaScript。它工作得很好,但它不支持从被调用的本机 Objective-C 函数返回值给调用 JavaScript 代码。

我很确定 Cordova (PhoneGap) 在某种程度上通过回调做到了这一点,但我一直很难理解底层机制是如何工作的。

有没有人遇到同样的问题并设法找到解决方案?

4

2 回答 2

4

现在,我从未使用过 WebViewJavascriptBridge,但我已经在 Objective-C 中使用简单的委托完成了这项工作,所以也许这会对您有所帮助:

MyScript.js

// requestFromObjc
// functionName (required):
//      the name of the function to pass along to objc
// returnedResult (not used by caller):
//      the result given by objc to be passed along
// callback (not used by caller):
//      sent by objc, name of the function to execute
function requestFromObjc(functionName, objcResult, callback)
{    
    if (!objcResult)
    {
        window.location = "myapp://objcRequest?function=" + functionName + "&callback=" + arguments.callee.name + "&callbackFunc=" + arguments.callee.caller.name;
    }
    else
    {
        window[callback](objcResult);
    }
}

function buttonClick(objcResult)
{    
    if (!objcResult)
    {
        // ask for the color from objc
        requestFromObjc("buttonColor&someParam=1");
    }
    else
    {
        // do something with result (in this case, set the background color of my div)
        var div = document.getElementById("someDiv");

        div.style.background = objcResult;
    }
}

我的页面.html

<html>
    <head>
        <script type="text/javascript" src="MyScript.js"></script>
    </head>
    <body>
        <div id="someDiv">
            This is my div! Do not make fun of it, though.
        </div>

        <button onClick="buttonClick(undefined)">
            Click Me!
        </button>
    </body>
</html>

视图控制器.m

-(NSString *) javaScriptResultForRequest:(NSDictionary *) params
{
    if ([params[@"function"] isEqualToString:@"buttonColor"])
    {
        NSArray *colors = @[ @"red", @"yellow", @"blue", @"green", @"purple" ];
        return colors[arc4random_uniform(colors.count)];
    }
    else
    {
        return @"undefined";
    }
}

-(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    if ([[[request URL] scheme] isEqualToString:@"myapp"])
    {
        // parse the URL here
        NSURL *URL = [request URL];

        if ([URL.host isEqualToString:@"objcRequest"])
        {
            NSMutableDictionary *queryParams = [NSMutableDictionary dictionary];
            NSArray *split = [URL.query componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"&="]];

            for (int i = 0; i < split.count; i += 2)
            {
                queryParams[split[i]] = split[i + 1];
            }

            NSString *result = [self javaScriptResultForRequest:queryParams];
            NSString *jsRequest = [NSString stringWithFormat:@"%@(\"%@\", \"%@\", \"%@\")", queryParams[@"callback"], queryParams[@"function"], result, queryParams[@"callbackFunc"]];

            // now we send this to the target
            [self.webView stringByEvaluatingJavaScriptFromString:jsRequest];
            return NO;
        }
    }

    return YES;
}

显然这比尝试在纯 JS 中执行等效功能要慢得多,因为它必须跳过所有循环。但是,如果您需要在您的 JS 代码中使用已经在您的 ObjC 代码中的东西,那么这可能适合您。

于 2012-09-12T15:48:05.123 回答
2

如果您的意思是“返回”,因为您的 JS 调用者将看到调用返回的结果,我不知道该怎么做。我怀疑这需要 JS 中没有的线程操作级别。相反,您可以重新编码 JS 端逻辑以创建结果处理函数。在伪代码中:

res = CallObjC(args);
work with res...

变成

CallObjC(args, function(res) { work with res...});

诚然有点尴尬,但习惯了 - 这是一种非常常见的模式。在内部,JS 桥接代码创建了请求到回调函数的映射。当 ObjC 代码有结果时,它使用 WebView 的 stringByEvaluatingJavaScriptFromString 调用定位并调用回调的 JS 桥代码。

@Richard - 请注意您发布的解决方案。将 window.location 设置为调用 shouldStartLoadWithRequest 会导致 webview 中的功能丢失以及 ObjectiveC 的消息丢失。当前的做法是使用 iframe 并拥有某种可以缓冲消息的消息队列。

于 2012-09-12T16:44:16.780 回答