后台的小伙伴要那种同步返回,可以用var直接接收返回值的形式进行处理逻辑,但是WkWebvie貌似不能用context进行JS注入。
1.先看一下UIWebView如何实现的
借助于iOS里的框架:JavaScriptCore.FrameWork来实现,
导入JavaScriptCore.FrameWork #import <JavaScriptCore/JavaScriptCore.h>
@protocolJSObjcDelegate<JSExport>
// AndroidWebView对象调用的JavaScript方法,必须声明!!!
-(int)indexOfMap;
@end
@interfaceViewController: UIViewController<UIWebViewDelegate,JSObjcDelegate>
@property(nonatomic, strong) JSContext *context;
@property(weak, nonatomic) IBOutletUIWebView*webView;
@end
加载webview就不写了,下面实现交互
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
// 获取context对象
self.context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//将AndroidWebView对象指向自身 js里面写window.AndroidWebView.indexOfMap() 就会调用原生里的indexOfMap方法
self.context[@"AndroidWebView"] = self;
self.context.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
context.exception = exceptionValue;
NSLog(@"异常信息:%@", exceptionValue);
};
// 获取到点击js按钮的事件
self.context[@"clickAction0"] = ^(){
NSLog(@"获取到点击js按钮的事件");
};
// oc调用js函数 并传参 js无返回值
NSString *jsAction = @"clickAction1(555)";
[self.context evaluateScript:jsAction];
// oc调用js函数 并传参 接收js返回值
NSString *str1 = [webView stringByEvaluatingJavaScriptFromString:@"clickAction2(666);"];
NSLog(@"js函数给我的返回值:%@", str1);
}
/**
待js调用
*/
- (int)indexOfMap {
NSLog(@"我被js调用了");
return 110;
}
JS调用:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
</head>
<body>
<div style="margin-top: 100px">
<h1>Objective-C和JavaScript交互</h1>
</div>
<div>
<input type="button" value="oc掉用js弹出提示->" onclick="clickAction0(11)">
<input type="button" value="js调用oc->" onclick="clickbtn()">
</div>
<script type="text/javascript">
// js函数
function clickAction0(typyId) {
alert(typyId)
}
function clickAction1(typyId) {
alert(typyId)
}
function clickAction2(typyId) {
alert(typyId);
return 'hello';
}
function clickbtn() {
var tempValue = window.AndroidWebView.indexOfMap();
alert(tempValue);
}
</script>
</body>
</html>
上面的那三种调用类似WKWebview中的
-(void)sendAPPInfo{
NSString *info = [NSString stringWithFormat:@"%@_%@",appId,appkey];
NSString * jsStr = [NSString stringWithFormat:@"clickAction0('%@')",info];
[self.webview evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
}];
}
JS调用
function clickAction0(info){
这里可以使用info进行逻辑处理
}
var tempValue = window.AndroidWebView.indexOfMap(); 这句就类似同步,方便js那边处理逻辑
---------------------------------------------------------------------------------------------------------------------------
2.WKWebView怎么做呢?
WKWebView默认对JavaScript下alert类的方法(包括alert(),confirm(),prompt())做了拦截,实现WKWebView的三个代理方法可拦截此方法。因为prompt方法H5应用的相对少,所以采用该方法进行拦截处理。
JS那边调用alert(),confirm(),prompt() 等函数,OC这边会走系统方法,如下的prompt方法
-(void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler
{
NSLog(@"prompt-JS传值:%@---%@",prompt,defaultText); //prompt,defaultText 就是JS传给我们的
NSString *info = [NSString stringWithFormat:@"%@_%@",appId,appkey];
completionHandler(info);//这里就是要返回给JS的返回值
}
JS实现,一句话OK了
var info = window.prompt(text,defaultText);
我们是SDK开发,基于后台更新,和小伙伴商量,让他用之前的方式,只是在获取参数的方法中写后续逻辑(不知道为毛H5那边不能用全局参数接收数据 ,能打印和使用貌似)
2021年补充:
去到新公司一看代码,这边使用的是js注入方式,需要双方规定handler,客户端去注册handler
//添加注入js方法, oc与js端对应实现
[config.userContentController addScriptMessageHandler:self name:@"jsAskInfo"];
[config.userContentController addScriptMessageHandler:self name:@"jsAskImage"];
[config.userContentController addScriptMessageHandler:self name:@"jsAskBack"];
[config.userContentController addScriptMessageHandler:self name:@"jsAskConsume"];
self.userContentController = config.userContentController;
在WKScriptMessageHandler的代理中去获取
#pragma mark - WKScriptMessageHandler
//实现js注入方法的协议方法
//依然是这个协议方法,获取注入方法名对象,获取js返回的状态值.
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
//找到对应js端的方法名,获取messge.body
__weak typeof(self)weakself = self;
if([message.name isEqualToString:@"jsAskInfo"]) {
-----------------------------------------------------------------------------------------------我之前采用的是单向规定message的方式,h5那边去执行某个message,我这边刚开始不需要知道message是什么无需注入,只需要在拦截的时候收到这个messgae的内容,去执行对应的代码,在WKNavigationDelegate的代理中实现
// 根据WebView对于即将跳转的HTTP请求头信息和相关信息来决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString * urlStr = navigationAction.request.URL.absoluteString;
NSLog(@"发送跳转请求:%@",urlStr);
NSURL *url = [NSURL URLWithString:urlStr];
//自己定义的协议头
NSString *htmlHeadString = @"weixin://";
NSString *htmlHeadString2 = @"alipay://";
NSString *htmlHeadString1 = @"dkmweb://openwin";
NSString *adStr = @"wdlink://ViewControllerWeb";
NSString *backStr = @"dkmweb://cancelBack";
NSString *SuccessStr = @"dkmweb://successBack";
NSString *telHeaderStr = @"tel:";
if([urlStr hasPrefix:telHeaderStr]){//致电
if ([[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlStr]]) {
[WSJieKouApi gameMaiDianWithEvent:@"open_phone_success" ext1:@""];//埋点
}
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
因此:现在的wkwebview交互有三种,简单描述一下:
1、js注入的方式,就类似双方规定一个函数如"gotoPay"。客户端需要注册这个"gotoPay"handler,然后在相关的代理中去接收"gotoPay"发过来的信息,并去执行下一步
,但这种方法类似注册通知一样,需要最后注销掉,容易遗忘
js 相关代码
window.webkit.messageHandlers.gotoPay.postMessage(action, token);
//action 和 token都可以用来传参
2、采用单向拦截方式,WKNavigationDelegate中代理decidePolicyForNavigationAction中去获取到对应的url的执行段,类似单向的。
3、采用执行弹框之类的三种代理方式,需h5端调用alert(),参数写在括号内,客户端就能在对应的代理中获取,并通过compHandler给h5返回信息。