iOS高级技术文章

JS和WKWebView、UIWebView交互

2018-12-04  本文已影响2人  宙斯YY

一、WKWebView(Apple推荐,性能好)

从需求出发,学习知识点,无需求驱动的学习都是无力的。


show.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);
}
上一篇下一篇

猜你喜欢

热点阅读