iOS下JS与Swift互相调用(二)WKWebView拦截URL

在上篇文章中介绍了UIWebView拦截URL的方式来处理JS与Swift交互:

iOS下JS与Swift互相调用(一)UIWebView拦截URL

由于UIWebView比较耗内存,性能上不太好,而苹果在iOS 8中推出了WKWebView,用WKWebView也可以拦截URL,做 JS 与 Swift 交互。关于WKWebView与UIWebView的性能对比,大家可以自行百度或者 Google

WKWebView 与 UIWebView 拦截URL 的处理方式基本一样, 除了代理方法和WKWebView的使用不太一样,关于 WKWebView 更详尽的讲解和用法,还是自行搜索学习,本文重点还是讲解如何实现 Swift 与 JS 互相调用

注意:WKWebView 是iOS 8 推出的 WebKit.framework 中的控件,只有 App 不需要兼容 iOS 7及以下的时候才可以使用。

1.创建 WKWebView,加载本地 HTML

WKWebView的创建有几点不同:

  • 初始化多了个configuration参数,当然这个参数我们也可以不传,直接使用默认的设置就好

  • WKWebView 有两个代理 navigationDelegate和UIDelegate,我们要拦截URL,是通过navigationDelegate的一个代理方法来实现,如果在HTML中要使用alert等弹窗,还必须得实现UIDelegate的相应代理方法

  • 在iOS 9之前,WKWebView加载本地HTML会有一些问题(不能加载本地HTML,或者部分CSS/本地图片加载不了等)

创建WKWebView的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func setupWKWebView() {
let configuration = WKWebViewConfiguration()
configuration.userContentController = WKUserContentController()
let preferences = WKPreferences()
preferences.javaScriptCanOpenWindowsAutomatically = true
preferences.minimumFontSize = 30.0
configuration.preferences = preferences
self.wkWebView = WKWebView(frame: self.view.frame, configuration: configuration)
let urlStr = Bundle.main.path(forResource: "index.html", ofType: nil)
let url = URL(fileURLWithPath: urlStr!)
self.wkWebView?.loadFileURL(url, allowingReadAccessTo: url)
self.wkWebView?.navigationDelegate = self
self.wkWebView?.uiDelegate = self
self.view.addSubview(self.wkWebView!)
}

因为加载的本地HTML内容,跟上一篇iOS下JS与Swift互相调用(一)UIWebView拦截URL中介绍的HTML内容一样,所以关于HTML中的内容就不再讲解了

2. 拦截URL

使用WKNavigationDelegate中的代理方法,拦截自定义的URL来实现 JS 调用 Swift 方法, 这里和UIWebView拦截 URL 一样

1
2
3
4
5
6
7
8
9
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.request.url?.scheme == "haleyaction" {
let url = navigationAction.request.url
handleCustomAction(url: url!)
decisionHandler(WKNavigationActionPolicy.cancel)
return
}
decisionHandler(WKNavigationActionPolicy.allow)
}

注意:如果实现了这个代理方法,就必须得调用 decisionHandler 这个block,否则会导致 App 崩溃, block参数是个枚举类型,WKNavigationActionPolicy.cancel代表取消加载,相当于UIWebView 的代理方法return false 的情况;
WKNavigationActionPolicy.allow 代表允许加载,相当于UIWebView的代理方法中 return true 的情况

然后根据不同 host 执行不同的 Native 代码,这和 UIWebView 一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func handleCustomAction(url:URL) {
let host = url.host!
switch host {
case "scanClick":
print("saoyisao")
case "shareClick":
share(url: url)
case "getLocation":
getLocation()
case "setColor":
changeBackGroundColor(url: url)
case "payAction":
payAction(url: url)
case "shake":
sharkeAction()
case "back":
goBack()
default:
break
}
}

3. JS和Swift的相互调用

JS调用Swift的原生方法,并且将结果返回给JS,这里的实现和 UIWebView 一样,WKWebView 提供了一个新的方法evaluateJavaScript:completionHandler: 实现Swift 调用 JS 的场景

1
2
3
4
5
6
7
8
9
func getLocation() {
//获取位置信息
//将获取的位置信息回调到js
let jsStr = "setLocation('广东省深圳市南山区')"
//WKWebView 提供了一个新的方法evaluateJavaScript:completionHandler:,实现原生 调用JS 等场景。
self.wkWebView?.evaluateJavaScript(jsStr, completionHandler: { (result, error) in
print("result:\(String(describing: result)) error:\(String(describing: error))")
})
}

evaluateJavaScript:completionHandler: 没有返回值,JS 执行成功还是失败会在completionHandler 中返回,所以使用这个API 就可以避免执行耗时的JS,或者 alert 导致界面卡住的问题

其他JS和Swift的相互调用和UIWebView一样

4.WKWebView中使用弹窗注意

在上面提到,如果在 WKWebView 中使用alert、confirm 等弹窗,就得实现WKWebView的WKUIDelegate中相应的代理方法,如果我们在JS中要显示 alert 弹窗,就必须实现如下代理方法,否则 alert 并不会弹出

1
2
3
4
5
6
7
8
9
10
11
//MARK: - WKUIDelegate
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
let alert = UIAlertController(title: "提醒", message: message, preferredStyle: .alert)
let action = UIAlertAction(title: "知道了", style:
.cancel) { (action) in
completionHandler()
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}

其中completionHandler这个block 一定得调用,至于在哪里调用,倒是无所谓,我们也可以写在方法实现的第一行,或者最后一行.

原文地址: iOS下 JS 与 OC 互相调用(二)–WKWebView 拦截URL

原文是讨论 OC 与 JS 的交互,我按照作者的思路,用 Swift 实现 Native 与 JS 的交互,这是 代码地址

本人刚开始写博客,主要是为了给自己的知识点做一个笔记,方便自己以后查阅,如果能让别人有所启发也是荣幸之至!如有错误,欢迎指正!