IOS: WKWebView和JS交互
前言
随着移动互联网的快速发展,Web开发与原生应用开发的界限正逐渐变得模糊。一方面,Web技术在不断的进步,许多先前只能在原生应用中实现的功能,如离线存储、设备API访问等,现在已经可以通过Web技术实现。另一方面,原生应用开发也开始采用Web技术,以提高开发效率和实现跨平台的能力。这种将Web开发与原生开发结合的趋势,为移动应用开发带来了全新的可能。
UIWebView与WKWebView的更迭
最初,iOS平台上加载网页的方式是通过使用UIWebView。然而,随着iOS平台技术的发展,UIWebView的局限性逐渐显现。首先,UIWebView的性能问题突出,它在加载网页时需要消耗大量的内存和CPU资源,导致应用运行效率低下。其次,UIWebView不支持并发加载多个网页,这在多任务处理的场景下显得力不从心。最后,UIWebView的API设计较为陈旧,不支持新的Web技术标准。
为了解决这些问题,苹果在iOS 8中引入了一个新的组件——WKWebView,用来取代UIWebView。WKWebView在设计时采用了Nitro JavaScript引擎,这是一个高效的引擎,能大大提高JavaScript的执行效率。同时,WKWebView还支持并发加载多个网页,并且采用了最新的Web技术标准,使得开发者能够使用最新的Web技术。
WKWebView与JavaScript的交互原理
WKWebView与JavaScript的交互,基本上可以分为两个方向:一是JavaScript调用Swift代码,二是Swift代码调用JavaScript代码。
对于JavaScript调用Swift代码,WKWebView提供了WKScriptMessageHandler
协议,开发者可以通过实现该协议的userContentController(_:didReceive:)
方法,来接收JavaScript代码通过postMessage
方法发送过来的消息。
对于Swift代码调用JavaScript代码,WKWebView提供了evaluateJavaScript(_:completionHandler:)
方法,开发者可以通过这个方法执行JavaScript代码,并通过completionHandler获取执行结果。
接下来通过一个例子来使用WKWebView与JavaScript的交互
1. 创建和配置WKWebView
首先,我们需要创建一个Swift项目,在默认生成ViewController
中引入WebKit模块,用于对WKWebView使用的支持。
import WebKit
让ViewController实现WKNavigationDelegate和WKScriptMessageHandler协议。WKNavigationDelegate协议用于处理网页导航事件,WKScriptMessageHandler协议则用于接收JavaScript发送的消息。此外,声明一个WKWebView类型的变量webView。
class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {
var webView: WKWebView!;
...
}
在loadView
方法中,我们首先创建了一个WKUserContentController
实例,并通过add(self, name: "nativeCallback")
方法添加了一个名为"nativeCallback"的message handler。然后,我们创建了一个WKWebViewConfiguration
实例,并将其userContentController
属性设置为我们刚才创建的content controller。最后,我们使用这个配置对象来创建WKWebView的实例,并将其赋值给view控制器的view属性。
override func loadView() {
let contentController = WKUserContentController();
contentController.add(self, name: "nativeCallback")
let config = WKWebViewConfiguration();
config.userContentController = contentController;
webView = WKWebView(frame: .zero, configuration: config);
webView.navigationDelegate = self;
view = webView;
}
注意由于这里没有在Interface Builder中使用Storyboard或Xib文件定义了视图的布局,因此需要重写loadView
方法,在这个方法中,需要创建一个UIView(或其子类)的实例,并赋值给视图控制器的view
属性。
在viewDidLoad
方法中,我们创建了一个URL请求,并通过webView.load(myRequest)
方法加载这个请求,用于加载本地服务器上的index.html文件
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string: "http://127.0.0.1:8080")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
2. 处理JavaScript调用
在userContentController(_:didReceive:)
方法中,我们处理了JavaScript代码发送的消息。当JavaScript代码通过window.webkit.messageHandlers.nativeCallback.postMessage(message)
调用"nativeCallback"的message handler时,这个方法就会被调用。我们在这个方法中检查消息的内容,并显示一个alert对话框。
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "nativeCallback" {
if let msg = message.body as? String {
showAlert(message: msg)
}
}
}
func showAlert(message: String) {
let alertController = UIAlertController(title: "提示", message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "确定", style: .default, handler: nil)
alertController.addAction(okAction)
present(alertController, animated: true, completion: nil)
}
3. 调用JavaScript方法
在WKNavigationDelegate的webView(_:didFinish:)
方法中,我们调用了JavaScript的displayDate()
方法来显示当前的日期。
swiftCopy code
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("displayDate()") { (any, error) in
if (error != nil) {
print(error ?? "err")
}
}
}
上述代码中在WKWebView
加载完index.html
页面后,swift会调用index.html的js方法displayDate()
方法来显示当前的日期。
4. Web页面
在index.html页面中,我们定义了两个JavaScript函数:displayDate()
和nativeCallback()
。displayDate()
函数会将当前的日期显示在id为"date"的p元素中。nativeCallback()
函数会通过webkit.messageHandlers.nativeCallback.postMessage('js调用ios')
发送一个消息到原生应用。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/>
<title>wkwebview</title>
<style type="text/css">
h1 {
color: red;
font-size: 1em;
}
body {
font-size: 2em;
}
button {
font-size: 0.5em;
}
p {
font-size: 0.5em
}
#tip {
color: blue;
}
</style>
</head>
<body>
<h1>WKWebview和JS交互</h1>
<div>
<p id="tip">ios调用js显示日期:</p>
<p id="date">这是一个段落</p>
</div>
<button type="button" onclick="nativeCallback()">js调用ios</button>
<script type="text/javascript">
function displayDate(){
document.getElementById("date").innerHTML=Date();
}
function nativeCallback() {
try {
//使用此方法,会报错,因此使用try-catch
webkit.messageHandlers.nativeCallback.postMessage('js调用ios');
} catch(error) {
console.log('WKWebView post message error: ', error);
}
}
</script>
</body>
</html>
通过上面4个步骤后,可以得到结果,当WKWebView
加载页面成功后,对调用index.html
的displayDate()·
方法来显示时间,点击页面上的按钮会弹出提示弹窗
以上就是在IOS中使用WKWebView
与JavaScript
进行交互的基本步骤。这种交互方式为原生提供更大的灵活性,我们可以利用Web技术的便利性,同时保持原生应用的高性能和流畅性。
需要注意的是,虽然WKWebView
提供了一套完整的API来和JavaScript进行交互,但我们仍然需要谨慎处理JavaScript发送的消息,防止潜在的安全问题。在实际开发中,我们应该尽量限制JavaScript能够调用的原生方法,并对传递的数据进行严格的验证和过滤。
同时,我们也需要注意WKWebView
的性能问题。虽然WKWebView
的性能大大优于UIWebView
,但它仍然不如原生视图。因此,应该根据应用的具体要求和性能要求,合理选择使用WKWebView
和原生视图。
转载自:https://juejin.cn/post/7232299098242105403