我一直在使用WebViewJavascriptBridge在iOS应用程序中桥接Objective-C和JavaScript。它工作得很好,但它不支持从被调用的本机Objective-C函数向调用JavaScript代码返回值。
我很确定Cordova(PhoneGap)在某种程度上通过回调做到了这一点,但我一直很难理解底层机制是如何工作的。
有没有人遇到过同样的问题,并设法找到了解决方案?
现在,我从未使用过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中执行等效函数要慢得多,因为它必须跳过所有循环。然而,如果您的JS代码中有一些东西需要使用,而这些东西已经在您的ObjC代码中了,那么这可能适合您。
如果你的意思是"return",即你的JS调用程序将看到调用返回的结果,我不知道该怎么做。我怀疑这需要JS中不可用的线程操作级别。相反,您可以重新编码JS端逻辑,以创建一个结果处理程序函数。在伪代码中:
res = CallObjC(args);
work with res...
成为
CallObjC(args, function(res) { work with res...});
诚然,这有点尴尬,但要习惯它——这是一种非常常见的模式。在内部,JS桥代码创建了一个请求到回调函数的映射。当ObjC代码得到结果时,它使用WebView的字符串ByEvaluatingJavaScriptFromString来调用定位并调用回调的JS桥代码。
@Richard-对你发布的解决方案要小心一点。将window.location设置为调用shouldStartLoadWithRequest可能会导致Web视图中的功能丢失,也会导致ObjectiveC的消息丢失。目前的做法是使用iframe并具有某种可以缓冲消息的消息队列。
WebViewJavascriptBridge
长期不维护。
也许你应该改用这个图书馆。
SDBridgeOC
self.bridge.consolePipeBlock = ^(id _Nonnull water) {
NSLog(@"Next line is javascript console.log------>>>>");
NSLog(@"%@",water);
};
这可以很容易地获得javascript console.log。
也有Swift语言版本。
SDBridgeSwift
bridge.consolePipeClosure = { water in
guard let jsConsoleLog = water else {
print("Javascript console.log give native is nil!")
return
}
print("Next line is Javascript console.log----->>>>>>>")
print(jsConsoleLog)
}
也有h5演示给你的合作伙伴。