JS和WKWebView、UIWebView交互
一、WKWebView(Apple推荐,性能好)
从需求出发,学习知识点,无需求驱动的学习都是无力的。
![](https://img.haomeiwen.com/i5734145/0c40af8f571931ee.gif)
需求:
1.根据淘宝提供的购物车链接获取到用户选择的商品信息,还要隐藏丑陋的导航条和Tabbar(原因是淘宝客类APP需要拼自己的PID进入订单才可以获取淘客收益)。
2.根据淘宝页面链接获取是否点击商品详情,如果点击则跳转到拼接自己的PID的淘宝详情,目的也是获取淘客收益。
实现思路:
1.注入js代码,根据html标签获取结点信息,通过添加响应事件回调到节点信息;
获取注入的js方法与本地代码进行交互,传递数据;
通过OC发送js方法,隐藏导航条和Tabbar。
2.获取跳转链接的url,进行前缀判定,满足详情则拦截原有链接,使用阿里百川SDK跳转手淘宝,不满足则不进行拦截。
基本交互方式:
创建一个WKWebView加载本地Html页面
@interface ViewController ()<WKUIDelegate,WKNavigationDelegate,WKScriptMessageHandler>
@property(nonatomic,strong) WKWebView * wkview;
@property(nonatomic,strong) JSContext * context;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
self.wkview = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
NSString *path = [[NSBundle mainBundle]pathForResource:@"test" ofType:@"html"];
NSString *html=[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[self.wkview loadHTMLString:html baseURL:nil];
self.wkview.navigationDelegate=self;
self.wkview.UIDelegate=self;
[self.view addSubview:self.wkview];
}
@end
Html部分
<!DOCTYPE html>
<html>
<head>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>这里填写标题</title>
<meta name="keywords" content="这里填写关键词" />
<meta name="description" content="这里填写说明内容" />
<script language="JavaScript" type="text/javascript">
<!--JS代码位置-->
function jstest2(){
alert('分享内容');
}
function jstest1(){
//传递多参数window.webkit.messageHandlers.jsx.postMessage({res:'res',status:1});
window.webkit.messageHandlers.jsx.postMessage('jstest1');
}
</script>
<style type="text/css">
<!--CSS样式代码位置-->
</style>
</head>
<body>
<input type="button" onclick="jstest1()" value="jstest1"/>
<input type="button" onclick="jstest2()" value="jstest2" />
<br/>
这里填写HTML代码
</body>
</html>
Native调用Js代码
//WKNavigation代理方法-网页加载完毕后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
//OC代码与JS进行交互 (打印title标题)
//注入JS代码
NSString *inputValueJS = @"document.title";
//执行JS
[webView evaluateJavaScript:inputValueJS completionHandler:^(id _Nullable response, NSError * _Nullable error) {
//value:这里填写标题 error:NULL
NSLog(@"value: %@ error: %@", response, error);
}];
}
Js调用Native方法
//1.配置jsx对象
{
[configuration.userContentController addScriptMessageHandler:self name:@"jsx"];
}
//2.WKScriptMessageHandler代理方法 - userContentController
{
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
//JS代码与OC进行交互
if ([message.name isEqualToString:@"jsx"]) {
NSString *mby = message.body;
NSLog(@"message.body%@",mby);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:mby preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action = [UIAlertAction actionWithTitle:@"ok" style:UIAlertActionStyleCancel handler:nil];
[alert addAction:action];
[self presentViewController:alert animated:YES completion:nil];
}
}
}
WK还可以注入自己定义的JS方法,然后在JS文件中使用并在Native中回调数据,在Native页面中使用。(也是回调淘宝购物车数据核心思想)
//1.创建WKUserScript对象注入
[configuration.preferences setValue:@YES forKey:@"allowFileAccessFromFileURLs"];
WKUserScript *script = [[WKUserScript alloc] initWithSource:@"function test(){return 'js注入'};" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
[configuration.userContentController addUserScript:script];
//2.更改一下jstest1方法
function jstest1(){
var str=test();
window.webkit.messageHandlers.jsx.postMessage(str);
}
//3.回调
{
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
//JS代码与OC进行交互
if ([message.name isEqualToString:@"jsx"]) {
NSString *mby = message.body;
NSLog(@"message.body%@",mby);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:mby preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action = [UIAlertAction actionWithTitle:@"ok" style:UIAlertActionStyleCancel handler:nil];
[alert addAction:action];
[self presentViewController:alert animated:YES completion:nil];
}
}
}
其实有了上述知识点,就可以实现需求1了呀,只不过具体Html结点解析,添加onclick事件,隐藏对应结点用具体JS代码实现就行了。
需求2相对简单一点,只需要实现一个WKNavigationDelegate代理方法就行了。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString *link = [navigationAction.request.URL.absoluteString stringByRemovingPercentEncoding];
[self getPid:link andBlock:^(NSString *url) {
if(url.length>0)
{
[self handleCustomAction:url];
//不允许跳转
decisionHandler(WKNavigationActionPolicyCancel);
return;
}else
{
//处理本页面打开连接不跳转的问题。
//判断是不是在本页面打开,如果不是,就重新 load一下
if (navigationAction.targetFrame == nil) {
[webView loadRequest:navigationAction.request];
}
//允许跳转
decisionHandler(WKNavigationActionPolicyAllow);
}
}];
}
如果url前缀在淘宝详情列表中,那么进行阿里百川SDK跳转,否则不拦截处理。
二、UIWebView(Apple摒弃,但为了兼容老SDK还需要用)
从项目角度考虑,使用WK做购物车回调没法满足真正的授权需求,因为淘宝授权信息只能通过WKWebView本身的Session处理,偶尔很不稳定,总需要登录,所以依赖于阿里百川SDK更靠谱,但是阿里百川跳转淘宝购物车,需要传一个UIWebView,而不是WKWebView(百川的SDK不更新了,当然人家其实并不提供这样的接口给你,只是你要做这个事情而已)。
基本交互方式
Html部分
function jstest2(){
alert('分享内容');
}
function jstest1(){
var str=test();
//window.webkit.messageHandlers.jsx.postMessage(str);
jsx.xxoo(str);
}
Native调用Js代码
[self.webview stringByEvaluatingJavaScriptFromString:@"function test(){return 'js注入xxxx'};"];
Js调用Native方法
1.实现JSExport协议
//定义一个和JS调起方法同名同参数的方法,测试发现,不支持多个参数,需封装成字典
@protocol WebViewJSExport <JSExport>
- (void)xxoo:(NSString*)res;
@end
2.实现部分
@interface ViewController ()<UIWebViewDelegate,WebViewJSExport>
@property(nonatomic,strong) UIWebView * webview;
@property(nonatomic,strong) JSContext * context;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.webview=[[UIWebView alloc]initWithFrame:self.view.bounds];
NSString *path = [[NSBundle mainBundle]pathForResource:@"test" ofType:@"html"];
NSString *html=[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[self.webview loadHTMLString:html baseURL:nil];
self.webview.delegate=self;
[self.view addSubview:self.webview];
self.context=[[JSContext alloc]init];
}
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
//使用Native注入JS代码
[self.webview stringByEvaluatingJavaScriptFromString:@"function test(){return 'js注入xxxx'};"];
//1.获取JS执行环境
JSContext *jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
self.context = jsContext;
//2.注入jsx对象(和WebView交互规则)
[self.context setObject:self forKeyedSubscript:@"jsx"];
self.context.exceptionHandler = ^(JSContext* context, JSValue* exceptionValue) {
context.exception = exceptionValue;
NSLog(@"异常信息:%@", exceptionValue);
};
}
//JS回调Native方法
-(void)xxoo:(NSString *)res
{
//回去Native注入JS的返回值
NSLog(@"%@",res);
}