现在的APP一般启动的使用都会首先打开广告页面,然后再跳转到首页。如果广告页和首页是同一个webview,那么存在一个问题,那就是在首页点击后退,会退到广告页面,这显然是不行的。如果禁用了网页滑动进行返回,那么这个问题就不存在了,但是如果这个广告页打开相关产品之后,在产品页有一个返回健,那么就必须处理这个问题了,不然将从产品页返回到广告页。
我解决这个问题的思路就是重新初始化webview,那么javscript的javascript:history.go(-1);
这个功能就会失效。
那么怎么从广告页重新初始化打开相关链接呢?这就是我们今天的主题,swift native与wkwbview中的javascript进行交互。
一,Native中开启wkwebview对javascript支持
为了支持从javascript脚本中可以调用native中的函数,首先需要开启wkwebview对javascript的支持。具体设置如下:
let config = WKWebViewConfiguration()// 创建配置 let per = WKPreferences() per.javaScriptEnabled = true//允许javascript per.javaScriptCanOpenWindowsAutomatically = true//允许JS自动调动window.open方法打开一个新的webView; config.preferences = per//将per设置到配置文件
从上面配置可以看出来,开启javascript功能归WKPreferences()
管理。
然后开启js向webview发送信息的方法。具体设置如下:
let userContent = WKUserContentController() // 创建UserContentController(提供JavaScript向webView发送消息的方法) userContent.add(self as WKScriptMessageHandler, name: "appModel")// 添加消息处理,注意:self指代的对象需要遵守WKScriptMessageHandler协议,结束时需要移除 config.userContentController = userContent// 将UserConttentController设置到配置文件
可以看出对于native与js交互的信息交互归WKUserContentController()
管理。
这里有一个关键点,那就是appModel
,这个我称为标识符,它就是与javascript交互的唯一标示,需要与html文件中的标示一致,这样native才能识别相关信息。
二,HTML中调用messageHandlers的方法
html中的相关代码为
var fileurl="https://www.nkbaishu.com" window.webkit.messageHandlers.appModel.postMessage(fileurl);
一般而言,我想都是响应click事件,然后激活上面的代码,关于这个语句有几点需要注意。
第一,从元素中直接调用js函数
<div onclick="clickadv()"></div> <script language="javascript"> function clickadv(){ try { window.webkit.messageHandlers.appModel.postMessage(fileurl); } catch(err){ window.open(fileurl); } }
比如上面的代码,封装一个clickadv函数,然后赋给div的onclick事件,这个代码是能够实现功能。
第二,直接把click写到js代码中去,我把上面的代码修改了,给div添加了一个id,然后在js代码中获取id,执行click事件。
代码如下:
<div id="clickdiv"></div> <script language="javascript"> var listbox = document.getElementById("clickdiv"); listbox.onclick=function(){ try { window.webkit.messageHandlers.appModel.postMessage(fileurl); } catch(err){ window.open(fileurl); } }
这个代码就十分完美了,在浏览器中只要点击id为“clickdiv”的div,在pc端执行try句块,失败,转而执行catch句块,所以在pc端不会报错;在app端则直接执行try句块,就会发送相关信息给wkwebview。
第三,有一点需要注意,上面的onclick="clickadv()"
事件和 id="clickdiv"
不能写在body
中,写在body中在pc端是可以实现功能,响应click事件,但是在wkwebview中不行,它不是html中的元素,这样将无法激活messageHandlers!开始我写在body中总是无法收到js的消息,折腾了我好久,NND。
三,Native中对javascript脚本发送的信息进行处理调用
做好了上面的工作之后,基本工作可以了,现在我们需要在native中添加处理这些信息的函数
extension ViewController:WKScriptMessageHandler {//用于与JS交互 func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { if (message.name == "appModel"){ if let myValue = message.body as? String{ print(myValue) } } } }
注意:ViewController需要和你的class类名一致!
这样就可以与js交流信息了。就我个人而言,我想把它放到类中,不想写扩展,那么可以这样干
class ViewController: UIViewController, WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate {}
把WKScriptMessageHandler添加到上面的class中,然后把userContentController函数丢到这个class类中就好了。
这个message.body
的内容就可以是我们的广告页产品的url了,获取到这个url,就可以重新初始化我们的webview,并webview.load(url)
了,这样就成功解决从产品页退回到广告页的问题了。
四,HTML中对返回键即javascript:history.go(-1);
失效的处理
在这里干脆还多一句嘴,从产品页返回广告页体验是不好,但是如果点击“后退”按钮没有任何反应,这个也是不好的。那么我们可以这样处理。
之所以这里的“后退”失效,是因为他的Refer
为空,那么我们可以在产品页加一个referrer的判断,如果为空就返回首页,这样逻辑就比较合理了。
<script type="text/javascript"> if (document.referrer === '') { var obj = document.getElementById("backlink"); obj.href="/"; } </script>
backlink
是返回按钮的id。
五,HTML广告页面中div被选中时闪烁,并且背景成深灰色问题
在app中的广告页面,当点击广告图片的时候,页面闪烁,整个背景变成了一个半透明的灰色背景,把div的轮廓明显呈现了出来,体验非常不好。这个问题的实质其实是手机上面的特有的touch问题,我们可以禁止页面被touch,这是h5设计的必修课吧。
把下面的代码添加到css中
div { -webkit-touch-callout: none; -webkit-user-select: none; -webkit-tap-highlight-color:transparent; //-webkit-tap-highlight-color: rgba(0,0,0,0); }
我干的就是禁止选择,禁止touch,tap时背景透明或者白色。这样用手点击页面时,这个深灰色背景就没有了。
再多说几句关于-webkit-tap-highlight-color,第一,这个属性只对ios有效,第二,关于他的4个0:rgba(0,0,0,0)
,4个0与rgba一一对应,r就是red,g就是green,b就是blue,a是alpha,rgb三个数的范围为0到255,a的值为0到1.对应中文意思就是红,绿,蓝和透明度。如果设置4个0,那么就是黑色的完全透明,就是禁用这个属性了。如果设置为(0,0,0,1),那么tap元素时就会呈现完全黑色底。如果设置为transparent,那么就是任何颜色都完全透明,相当于最后一个0位置的作用。
六,Native中对WKScriptMessageHandler内存泄露的处理
在这里还有很重要的一点,那就是对WKScriptMessageHandler内存泄露的处理,上面添加了handle之后,在我们的ViewController销毁的时候必须清除我们添加的这个handle,否则将内存泄露。
处理方法有2个。
第一个办法,移除handle,我们可以把这个移除代码放到viewWillDisappear重写函数中,
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) //当前ViewController销毁前将其移除,否则会造成内存泄漏 self.nwebview?.configuration.userContentController.removeScriptMessageHandler(forName: "appModel") //移除方法 }
第二个办法,重写内存报警函数
override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() }
也可以二者结合,这样处理之后就ok了。
七,Native中wkwebview对html网页执行javascript脚本的方法
这个就比较简单了,我们一般等页面加载完成之后再来执行javascript代码。那么我们可以把代码放在 didFinish
中处理。
比如,我们让每个页面都禁止或显示系统默认菜单,可以这样干
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { webview.evaluateJavaScript("document.documentElement.style.webkitTouchCallout='none';", completionHandler: nil) }
可见就是利用wkwebview的evaluateJavaScript方法来执行js代码,completionHandler
参数可以省略。
好了,swift中关于wkwebview与javascript脚本的交互就介绍到这里了。