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;
    }
}

MyPage.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>

ViewController.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 で同等の機能を実行しようとするよりもはるかに遅くなります。これは、すべてのループをジャンプする必要があるためです。ただし、既に ObjC コードにあるものを JS コードで使用する必要がある場合は、これが適している可能性があります。

于 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 に答える