iOS开发-------基于WKWebView的原生与JavaScript数据交互
<p>https://blog.csdn.net/runintolove/article/details/52064268</p><p> </p>
<p> </p>
<div class="article-header-box">
<div class="article-header">
<div class="article-info-box">
<div class="operating">版权</div>
</div>
</div>
</div>
<div id="article_content" class="article_content clearfix">
<div id="content_views" class="markdown_views">
<pre class="prettyprint"><code class="has-numbering">WKWebView是iOS8.0之后用以替代UIWebView的网页浏览器,包含在WebKit中,可以通过 @import WebKit 导入。
如果工程需要适配iOS7,那么请在iOS7中使用UIWebView。
如果是iOS8.0以上,请果断的选择WKWebView吧,无论是从功能,加载速度还是性能上,它都是不二的选择。
</code></pre>
<ul class="pre-numbering">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<p><br>毕业回公司有段时间了,与其说比较忙,不如说最近接触的东西有点小多,并且还是多数自己之前闻所未闻的,整个人就显得比较浮躁,所以就没有对见识到的东西进行整理,感觉挺对不住自己的,知错就改,之后会慢慢的将看到的、学到的比较好的东西进行整理,记录一下,希望能在帮助俺那不靠谱的记性同时,也能够帮助有同样困惑的小伙伴。</p>
<p>不过这里并不会非常具体的介绍WKWebView如何使用以及各种协议对象是什么作用,毕竟Google一下就会有很多介绍WKWebView的文章,并且他们都写得很好很详细,大家感兴趣的可以Google一下。给大家推荐一个WKWebView的新特性与使用</p>
<p>这里记录的交互仅仅的是进行一些数据的交互,对于其他的UI交互以及响应交互,请查看一下上面推荐的博文,写的真的很详细;如果大家有更好的交互方式,也麻烦大家告知一下<code>3Q</code></p>
<p> </p>
<h1 id="ios客户端-web端">iOS客户端 -> Web端</h1>
<p>言归正传,我们用WKWebView加载一个HTML文件(加载网络网页其实是一个道理的),万一进行某个操作的时候需要原生给web传递一个数据(至于什么数据,需要根据具体的需求来确定),这里就以一个字符串进行举例: <br><br>在需要与Web进行复杂交互的时候,通常都需要在实例化<code>WKWebView</code>的之前,先实例化一个<code>WKWebView</code>的配置对象(<code>WKWebViewConfiguration</code>类型),对javaScript的注入第一步就是需要处理一下这个配置对象:</p>
<pre name="code" class="prettyprint"><code class="language-Swift hljs cs has-numbering"><span class="hljs-comment">//初始化webView的配置对象
<span class="hljs-keyword">let configuration = WKWebViewConfiguration()
<span class="hljs-comment">//比如这就是需要传递给web的参数
<span class="hljs-keyword">let name = <span class="hljs-string">"RITL"
<span class="hljs-comment">//声明一个WKUserScript对象
<span class="hljs-keyword">let script:WKUserScript = WKUserScript(source: <span class="hljs-string">"function callJavaScript() {ObjCToJavaScript('\(name)');}", injectionTime: .AtDocumentStart, forMainFrameOnly: <span class="hljs-keyword">true)
<span class="hljs-comment">//对Script对象进行添加
configuration.userContentController.addUserScript(script)</span></span></span></span></span></span></span></span></span></span></code></pre>
<ul class="pre-numbering">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
</ul>
<p><br>因为自己的Demo中的触发点在于导航栏中的Do按钮(开发中,这个触发点是由实际需求确定的):</p>
<pre name="code" class="prettyprint"><code class="language-Swift hljs cs has-numbering"><span class="hljs-comment">//响应Do
@IBAction func doTap(sender: AnyObject)
{
<span class="hljs-comment">//调用的JS方法,执行
<span class="hljs-keyword">let js = <span class="hljs-string">"callJavaScript()";
webView.evaluateJavaScript(js) { (<span class="hljs-keyword">object, error) <span class="hljs-keyword">in
}<span class="hljs-comment">//与iOS8之前的UIWebView类似
}</span></span></span></span></span></span></span></code></pre>
<ul class="pre-numbering">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
</ul>
<p>It’s OK? 总感觉还是还差一步的,既然有<code>addXXX</code>这句,是不是应该有<code>removeXXX</code>呢,还真有,也就差这么一步</p>
<pre name="code" class="prettyprint"><code class="language-Swift hljs avrasm has-numbering">deinit
{
//删除注入的JS
webView<span class="hljs-preprocessor">.configuration<span class="hljs-preprocessor">.userContentController<span class="hljs-preprocessor">.removeAllUserScripts()
}</span></span></span></code></pre>
<ul class="pre-numbering">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<p><br>Demo的HTML语句比较low,但仅仅的就是为了测试,所以就忍了吧0.0</p>
<pre name="code" class="prettyprint"><code class="language-HTML hljs xml has-numbering"><span class="hljs-doctype"><!DOCTYPE html>
<span class="hljs-tag"><<span class="hljs-title">html>
<span class="hljs-tag"><<span class="hljs-title">head>
<span class="hljs-tag"><<span class="hljs-title">meta <span class="hljs-attribute">charset=<span class="hljs-value">"utf-8" />
<span class="hljs-tag"><<span class="hljs-title">title>我是HTML标题<span class="hljs-tag"></<span class="hljs-title">title>
<span class="hljs-tag"></<span class="hljs-title">head>
<span class="hljs-tag"><<span class="hljs-title">script><span class="javascript">
<span class="hljs-comment">//原生调用该方法,并通过接收传入的参数进行下一步操作
<span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-title">ObjCToJavaScript<span class="hljs-params">(name)
{
<span class="hljs-comment">//修改label显示的title
document.getElementById(<span class="hljs-string">"Text").innerText = name;
}
<span class="hljs-tag"></<span class="hljs-title">script>
<span class="hljs-tag"><<span class="hljs-title">body>
<span class="hljs-tag"><<span class="hljs-title">label <span class="hljs-attribute">id =<span class="hljs-value">"Text" <span class="hljs-attribute">style = "<span class="hljs-attribute">margin-top: <span class="hljs-attribute">100px ; <span class="hljs-attribute">display: <span class="hljs-attribute">block; <span class="hljs-attribute">font-size:100px;">Text<span class="hljs-tag"></<span class="hljs-title">label>
<span class="hljs-tag"></<span class="hljs-title">body>
<span class="hljs-tag"></<span class="hljs-title">html>
<span class="hljs-comment"><!--总结一下,其实从上面的描述也就看出来了,所谓的原生对JS进行传值的实质说白了就是修改了响应JS的触发点></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<ul class="pre-numbering">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
<li>15</li>
<li>16</li>
<li>17</li>
<li>18</li>
<li>19</li>
<li>20</li>
<li>21</li>
<li>22</li>
</ul>
<p>最后看一下传值交互的效果,由客户端将字符串<code>“RITL"</code>传递给web,经由JS方法修改label标签的值:</p>
<div align="center"><img src="https://img-blog.csdn.net/20160729141325471"></div>
<p> </p>
<h1 id="web端-ios客户端">Web端 -> iOS客户端</h1>
<p>这个传值方向是目前为止,我在项目中应用的比较广泛的一种,在<code>WKWebView</code>之前(<code>UIWebView</code>),想要获得JS中对客户端传的参数值,基本有方法有如下两种: </p>
<ol>
<li>
<p>web端通过重定向,将传递的参数拼接成自定义的格式,将参数作为url进行重新定向,客户端通过实现WebView的代理方法获取到重定向的url,通过解析字符串进而获得传出的参数(0.0 是不是觉得好low啊);</p>
</li>
<li>
<p>借助大神写好的第三方库,比如:JavaScriptWebView等完成WebView与JS间的传值。但这里也说一下自己的看法,这种情况多数是将WebView的代理以及控制权交给了三方中的某个管理类,能完成信息交互的同时也表示着我们失去了对WebView的信息交互控制权,个人觉得不是很爽。当然,除此之外还有什么办法呢T^T。</p>
<pre class="prettyprint"><code class="has-numbering">相比UIWebView,WKWebView中就为我们提供了看起来更加高大上同时也让我们不失去对WebView控制权的交互方法,真所谓一举两得。
</code></pre>
<ul class="pre-numbering">
<li>1</li>
</ul>
</li>
</ol>
<p>与第一种方向相同,同样操作webView的配置对象,在WKWebView的配置对象中对javaScript交互数据进行监听,方法如下:</p>
<pre name="code" class="prettyprint"><code class="language-Objective-C hljs rust has-numbering"><span class="hljs-comment">//webView的配置对象对传出数据的名字进行监听,此时负责接收JS消息处理的对象不要忘记履行协议<WKScriptMessageHandler>
[<span class="hljs-keyword">self.webView.configuration.userContentController addScriptMessageHandler:<span class="hljs-keyword">self name:name];</span></span></span></code></pre>
<ul class="pre-numbering">
<li>1</li>
<li>2</li>
</ul>
<p><br><code><WKScriptMessageHandler></code>协议也比较给力,只有一个协议方法:</p>
<pre name="code" class="prettyprint"><code class="language-Objective-C hljs cs has-numbering"><span class="hljs-preprocessor">#<span class="hljs-keyword">pragma mark - <WKScriptMessageHandler>
<span class="hljs-comment">//通过接收JS传出消息的name进行捕捉的回调方法
- (<span class="hljs-keyword">void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
<span class="hljs-keyword">if()<span class="hljs-comment">//此处name为JS传出信息打包的标志<name>
{
<span class="hljs-comment">//用message.body获得JS传出的参数体
<span class="hljs-comment">//handle coding..
}
}</span></span></span></span></span></span></span></span></code></pre>
<ul class="pre-numbering">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
</ul>
<p>Web端在需要客户端配合的时候通过如下代码进行触发:</p>
<pre name="code" class="prettyprint"><code class="language-JavaScript hljs avrasm has-numbering"><span class="hljs-comment">/* 一个抽象模型 */
window<span class="hljs-preprocessor">.webkit<span class="hljs-preprocessor">.messageHandlers.
<name><span class="hljs-preprocessor">.postMessage(<messageBody>)
<span class="hljs-comment">/* 具体实例 */
<span class="hljs-comment">/*JS的传出语句如下,那么name = "RITL"*/
window<span class="hljs-preprocessor">.webKit<span class="hljs-preprocessor">.messagehandlers<span class="hljs-preprocessor">.RITL<span class="hljs-preprocessor">.postMessage(<span class="hljs-string">"RITL-GOGOGO")
<span class="hljs-comment">/*外部要想获得上面的信息则进行如下监听*/
<span class="hljs-comment">;
<span class="hljs-comment">/*获得传出的字符串参数,即"RITL-GOGOGO"*/
NSString * dataString = message<span class="hljs-preprocessor">.body<span class="hljs-comment">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<ul class="pre-numbering">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
</ul>
<p><br>预定javaScript处理结束了,也能完成各种交互动作,如果这个时候大家能想得到<code>add必有remove的规则</code>,我表示此时内心无比欣慰,突然感觉这篇博文真的没有白写,那么习惯性的在<code>dealloc</code>中进行监听注销吧:</p>
<pre name="code" class="prettyprint"><code class="language-Objective-C hljs java has-numbering"><span class="hljs-javadoc">/** 注销 */
;</span></code></pre>
<ul class="pre-numbering">
<li>1</li>
<li>2</li>
</ul>
<p><br><span style="color: rgba(255, 165, 0, 1)">BUT!!!!!</span></p>
<p> </p>
<h1 id="内存泄露">内存泄露</h1>
<p>通常我们认为上面的做法已经很好的完成需求,如果是仔细或者对系统性能比较关注的开发者,相信肯定会在dealloc中打一个断点来求证一下,那么这个时候我们就会发现,Dealloc根本不走!没错,根本不走!</p>
<p>PS: (这一点让我想起来之前项目中NSTimer造成的内存泄露问题,跟上面的问题简直是异曲同工之理(额,原理可能不一样,但效果是一样的,就是当前的控制器不会释放,被强引用了,So? 自然也不会走dealloc方法))。</p>
<p>难不成<code>-addScriptMessageHandler:name:</code>个方法会对Handler进行强引用?要不换成<code>__weak</code>吧? No!!!! NSTimer造成内存泄露的时候换成归零弱引用好使么,不好使吧!那么解决方法就和解决NSTimer的方法类似了。</p>
<p>通过转移代理对象(额,其实就是引用对象)来完成强引用的转移,从而让当前控制器得以释放,进而remove掉messagehandler, 完成对转移代理对象释放,将内存泄露堵住:</p>
<p>下面是我解决响应方法的一个实现类:</p>
<pre name="code" class="prettyprint"><code class="language-Objective-C hljs java has-numbering"><span class="hljs-comment">/*CB_YZZBScriptMessageHandler.h*/
#<span class="hljs-keyword">import <Foundation/Foundation.h>
<span class="hljs-annotation">@import WebKit;
NS_ASSUME_NONNULL_BEGIN
<span class="hljs-annotation">@interface CB_YZZBScriptMessageHandler : NSObject<WKScriptMessageHandler>
<span class="hljs-annotation">@property (nullable, nonatomic, weak)id <WKScriptMessageHandler> delegate;
<span class="hljs-javadoc">/** 创建方法 */
- (instancetype)initWithDelegate:(id <WKScriptMessageHandler>)delegate;
<span class="hljs-javadoc">/** 便利构造器 */
+ (instancetype)scriptWithDelegate:(id <WKScriptMessageHandler>)delegate;;
<span class="hljs-annotation">@end
NS_ASSUME_NONNULL_END
<span class="hljs-comment">/*CB_YZZBScriptMessageHandler.m*/
<span class="hljs-annotation">@implementation CB_YZZBScriptMessageHandler
-(instancetype)initWithDelegate:(id<WKScriptMessageHandler>)delegate
{
<span class="hljs-keyword">if (self = [<span class="hljs-keyword">super init])
{
_delegate = delegate;
}
<span class="hljs-keyword">return self;
}
+(instancetype)scriptWithDelegate:(id<WKScriptMessageHandler>)delegate
{
<span class="hljs-keyword">return [initWithDelegate:delegate];
}
#pragma mark - <WKScriptMessageHandler>
- (<span class="hljs-keyword">void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
;
}
<span class="hljs-annotation">@end</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<ul class="pre-numbering">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
<li>15</li>
<li>16</li>
<li>17</li>
<li>18</li>
<li>19</li>
<li>20</li>
<li>21</li>
<li>22</li>
<li>23</li>
<li>24</li>
<li>25</li>
<li>26</li>
<li>27</li>
<li>28</li>
<li>29</li>
<li>30</li>
<li>31</li>
<li>32</li>
<li>33</li>
<li>34</li>
<li>35</li>
<li>36</li>
<li>37</li>
<li>38</li>
<li>39</li>
<li>40</li>
<li>41</li>
<li>42</li>
<li>43</li>
<li>44</li>
<li>45</li>
<li>46</li>
<li>47</li>
<li>48</li>
<li>49</li>
<li>50</li>
<li>51</li>
<li>52</li>
</ul>
<p>使用的时候转一下强引用对象就OK啦</p>
<pre name="code" class="prettyprint"><code class="language-Objective-C hljs rust has-numbering"><span class="hljs-comment">//实例引用对象
CB_YZZBScriptMessageHandler * messageHandle = ;
<span class="hljs-comment">//注册JS信息处理
[<span class="hljs-keyword">self.webView.configuration.userContentController addScriptMessageHandler:messageHandle name:@<span class="hljs-string">"RITL"];</span></span></span></span></span></code></pre>
<ul class="pre-numbering">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<p> </p>
<h1 id="取消长按响应">取消长按响应</h1>
<pre name="code" class="prettyprint"><code class="hljs objectivec has-numbering">- (<span class="hljs-keyword">void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
{
<span class="hljs-comment">//注入不响应的JS方法即可
;
}</span></span></span></span></span></code></pre>
</div>
</div><br><br>
来源:https://www.cnblogs.com/itlover2013/p/14246459.html
頁:
[1]