OC与JS交互之WKWebView

上一篇文章我们使用了JavaScriptCore框架UIWebView的示例,iOS8苹果偏爱HTML5,重构了UIWebVIew,给我们带来了WKWebView,使其性能、稳定性、功能大幅度提升,也更好的支持了HTML5的新特性。这篇文章就们就拿WKWebView来小试牛刀,这也是我的学习过程。
我们先来看一下学习这个WKWebView的主要类别

阅读目录

  • 一、WKWebView Framework
  • 二、WKWebView中的三个代理方法
  • 三、使用WKWebView重写
  • 四、后记

WKWebView的14个类与3个协议:

WKBackForwardList: 之前访问过的 web 页面的列表,可以通过后退和前进动作来访问到。

WKBackForwardListItem: webview 中后退列表里的某一个网页。

WKFrameInfo: 包含一个网页的布局信息。

WKNavigation: 包含一个网页的加载进度信息。

WKNavigationAction: 包含可能让网页导航变化的信息,用于判断是否做出导航变化。

WKNavigationResponse: 包含可能让网页导航变化的返回内容信息,用于判断是否做出导航变化。

WKPreferences: 概括一个 webview 的偏好设置。

WKProcessPool: 表示一个 web 内容加载池。 

WKUserContentController: 提供使用 JavaScript post 信息和注射 script 的方法。

WKScriptMessage: 包含网页发出的信息。

WKUserScript: 表示可以被网页接受的用户脚本。 

WKWebViewConfiguration: 初始化 webview 的设置。

WKWindowFeatures: 指定加载新网页时的窗口属性。

WKWebsiteDataStore: 包含网页数据存储和查找。

 

WKNavigationDelegate: 提供了追踪主窗口网页加载过程和判断主窗口和子窗口是否进行页面加载新页面的相关方法。

WKUIDelegate: 提供用原生控件显示网页的方法回调。

WKScriptMessageHandler: 提供从网页中收消息的回调方法。

二、WKWebView中的三个代理方法

  1. WKNavigationDelegate
    该代理提供的方法,可以用来追踪加载过程(页面开始加载、加载完成、加载失败)、决定是否执行跳转。
// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;

页面跳转的代理方法有三种,分为(收到跳转与决定是否跳转两种)

// 接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
  1. WKUIDelegate
    创建一个新的WKWebView
// 创建一个新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;

剩下三个代理方法全都是与界面弹出提示框相关的,针对于web界面的三种提示框(警告框、确认框、输入框)分别对应三种代理方法。

// 界面弹出警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void (^)())completionHandler;
// 界面弹出确认框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
// 界面弹出输入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler;
  1. WKScriptMessageHandler
    这个协议中包含一个必须实现的方法,这个方法是native与web端交互的关键,它可以直接将接收到的JS脚本转为OC或Swift对象。
// 从web界面中接收到一个脚本时调用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;

三、使用WKWebView

我这里加载的是本地的html,我先贴出html的代码

<!DOCTYPE html>
<html>
<head>

    <meta charset="utf-8" http-equiv="Content-Type" content="text/html">

    <title>小红帽</title>

    <style>
        *{
             font-size: 50px;
        }

        .div{
            align:"center";
         }
    
        .btn{
            height:80px; width:80%; padding: 0px 30px 0px 30px; background-color: #0071E7; border: solid 1px #0071E7; border-radius:5px; font-size: 1em; color: white
        }
    </style>

    <script>
        function clear(){
            document.getElementById('mobile').innerHTML=''
            document.getElementById('name').innerHTML=''
            document.getElementById('msg').innerHTML=''
        }
       //oc调用js的方法列表
       function alertMobile(){
               //这里已经调用过来了 但是搞不明白为什么alert方法没有响应
                //alert('我是上面的小黄 手机号是:13300001111')
             document.getElementById('mobile').innerHTML='我是上面的小黄 手机号是:13300001111'
       }

      function alertName(msg){
             document.getElementById('name').innerHTML='你好 ' + msg + ', 我也很高兴见到你'
      }
      
      function alertSendMsg(num,msg){
        document.getElementById('msg').innerHTML='这是我的手机号:' + num + ',' + msg + '!!'
      }

      //JS响应方法列表
      function btnClick1(){
        window.webkit.messageHandlers.showMobile.postMessage(null)
        //window.webkit.messageHandlers.showMobile.postMessage(null)
      }

      function btnClick2(){
        window.webkit.messageHandlers.showName.postMessage('xiao黄')
      }

      function btnClick3(){
        window.webkit.messageHandlers.showSendMsg.postMessage(['13300001111', 'Go Climbing This Weekend !!!'])
      }

    </script>

</head>

<body>
    <br/>
    <div>
        <label>自己写html</label>
    </div>
    <br/>
    <div id="mobile"></div>
    <div class="div">
        <button class="btn" type="button" onclick="btnClick1()">小红帽的手机号</button>
    </div>
    <br/>
    <div id="name"></div>
    <div class="div">
        <button class="btn" type="button" onclick="btnClick2()">打电话给小红帽</button>
    </div>
    <br/>
    <div id="msg"></div>
    <div class="div">
        <button class="btn" type="button" onclick="btnClick3()">发短信给小红帽</button>
    </div>
    <br/>

</body>

</html>

关于html的内容,我在这里不多加解释,有兴趣的同学可以去学习一下关于h5,css,javascript的相关知识。

WKWebView不支持nib文件,所以这里需要使用代码初始化并加载WebView

/*设置configur对象的WKUserContentController属性的信息,也就是设置js可与webview内容交互配置
     1、通过这个对象可以注入js名称,在js端通过window.webkit.messageHandlers.自定义的js名称.postMessage(如果有参数可以传递参数)方法来发送消息到native;
     2、我们需要遵守WKScriptMessageHandler协议,设置代理,然后实现对应代理方法(userContentController:didReceiveScriptMessage:);
     3、在上述代理方法里面就可以拿到对应的参数以及原生的方法名,我们就可以通过NSSelectorFromString包装成一个SEL,然后performSelector调用就可以了
     4、以上内容是WKWebview和UIWebview针对JS调用原生的方法最大的区别(UIWebview中主要是通过是否允许加载对应url的那个代理方法,通过在js代码里面写好特殊的url,然后拦截到对应的url,进行字符串的匹配以及截取操作,最后包装成SEL,然后调用就可以了)
     */
    
    /*
     上述是理论说明,结合下面的实际代码再做一次解释,保你一看就明白
     1、通过addScriptMessageHandler:name:方法,我们就可以注入js名称了,其实这个名称最好就是跟你的方法名一样,这样方便你包装使用,我这里自己写的就是openBigPicture,对应js中的代码就是window.webkit.messageHandlers.openBigPicture.postMessage()
     2、因为我的方法是有参数的,参数就是图片的url,因为点击网页中的图片,要调用原生的浏览大图的方法,所以你可以通过字符串拼接的方式给"openBigPicture"拼接成"openBigPicture:",我这里没有采用这种方式,我传递的参数直接是字典,字典里面放了方法名以及图片的url,到时候直接取出来用就可以了
     3、我的js代码中关于这块的代码是
     window.webkit.messageHandlers.openBigPicture.postMessage({methodName:"openBigPicture:",imageSrc:imageArray[this.index].src});
     4、js和原生交互这块内容离不开
     - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{}这个代理方法,这个方法以及参数说明请到下面方法对应处
    
    */


WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc]init];
    //设置configur对象的preferences属性的信息
    config.preferences.minimumFontSize = 18;
    
    WKWebView *wkView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, 414, 735/2) configuration:config];
    wkView.navigationDelegate=self;
    [self.view addSubview:wkView];
    self.wkwebView = wkView;
    
    
    NSString *filePath=[[NSBundle mainBundle] pathForResource:@"myindex" ofType:@"html"];
    NSURL *baseUrl=[[NSBundle mainBundle] bundleURL];
    [self.wkwebView loadHTMLString:[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil] baseURL:baseUrl];
    
    WKUserContentController *userController=config.userContentController;
    
    //JS调用OC 添加处理脚本
    [userController addScriptMessageHandler:self name:@"showMobile"];
    [userController addScriptMessageHandler:self name:@"showName"];
    [userController addScriptMessageHandler:self name:@"showSendMsg"];

在代理方法中处理相关的操作,js调用oc的代码

//js调用oc,通过这个代理方法进行拦截
/*
 1、js调用原生的方法就会走这个方法
 2、message参数里面有2个参数我们比较有用,name和body,
 2.1 :其中name就是之前已经通过addScriptMessageHandler:name:方法注入的js名称
 2.2 :其中body就是我们传递的参数了,比如说我在js端传入的是一个字典,所以取出来也是字典
 */

-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    if ([message.name isEqualToString:@"showMobile"]) {
        [self alertMessage:@"这是下面的小红帽 手机号 123333333"];
    }
    if ([message.name isEqualToString:@"showName"]) {
        NSString *info=[NSString stringWithFormat:@"%@",message.body];
        [self alertMessage:info];
    }
    if ([message.name isEqualToString:@"showSendMsg"]) {
        NSArray *arr=message.body;
        NSString *info=[NSString stringWithFormat:@"%@%@",arr.firstObject,arr.lastObject];
        [self alertMessage:info];
    }
}
-(void)alertMessage:(NSString *)msg{
    
    UIAlertController *alertController=[UIAlertController alertControllerWithTitle:@"信息" message:msg preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction *ok=[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        
    }];
    
    [alertController addAction:ok];
    [self presentViewController:alertController animated:YES completion:^{
        
    }];
    
}

下面的是oc调用js,分开写主要是为了让大家看清楚每部分的代码,已经相关的解释

- (IBAction)clearBtn:(id)sender {
    [self.wkwebView evaluateJavaScript:@"clear()" completionHandler:nil];
}
//oc调用js,通过evaluateJavaScript:注入方法名
- (IBAction)clickBtnItem:(UIButton *)sender {
    switch (sender.tag) {
        case 100:
        {
            [self.wkwebView evaluateJavaScript:@"alertMobile()" completionHandler:nil];
        }
            break;
            
        case 101:
        {
            [self.wkwebView evaluateJavaScript:@"alertName('小红毛')" completionHandler:nil];
        }
            break;
            
        case 102:
        {
            [self.wkwebView evaluateJavaScript:@"alertSendMsg('18870707070','周末爬山真是件愉快的事情')" completionHandler:nil];
        }
            break;
            
        default:
            break;
    }
}

这是我拿出html里面javaScript的代码,供大家阅读,里面我都有标注的注释。

<script>
        function clear(){
            document.getElementById('mobile').innerHTML=''
            document.getElementById('name').innerHTML=''
            document.getElementById('msg').innerHTML=''
        }
       //oc调用js的方法列表
       function alertMobile(){
               //这里已经调用过来了 但是搞不明白为什么alert方法没有响应
                //alert('我是上面的小黄 手机号是:13300001111')
             document.getElementById('mobile').innerHTML='我是上面的小黄 手机号是:13300001111'
       }

      function alertName(msg){
             document.getElementById('name').innerHTML='你好 ' + msg + ', 我也很高兴见到你'
      }
      
      function alertSendMsg(num,msg){
        document.getElementById('msg').innerHTML='这是我的手机号:' + num + ',' + msg + '!!'
      }

      //JS响应方法列表
      function btnClick1(){
        window.webkit.messageHandlers.showMobile.postMessage(null)
        //window.webkit.messageHandlers.showMobile.postMessage(null)
      }

      function btnClick2(){
        window.webkit.messageHandlers.showName.postMessage('xiao黄')
      }

      function btnClick3(){
        window.webkit.messageHandlers.showSendMsg.postMessage(['13300001111', 'Go Climbing This Weekend !!!'])
      }

    </script>

四、学习总结

到此,关于js和原生的交互系列的示例已完成,过程中遇到很多坑,但也很有收获,关于我的描述不清,或者不妥的地方,请大家指出。每篇文章都会对知识点进行总结,在文章末尾给出相关链接和示例DEMO的地址,同样本文的示例也已放在GitHub上,需要的同学取走不谢。关于这几篇文章的DEMO,对比学习,在看的过程中有什么呢疑问,欢迎在下面留言,若发现文章中有那些地方没有阐述清,或者没有提到也可以留言告诉我,我们公司最近一直是h5和原生的app想结合,所以研究一段时间。随着H5的强大,hybrid app已经成为当前互联网的大方向,单纯的native app和web app在某些方面显得就很劣势。
  
戳这里:本文的DEMO地址欢迎star

参考资料(戳这里):

http://nshipster.cn/wkwebkit/
http://www.huangyibiao.com/archives/742

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容