在 iOS 开发中,实现原生与 WKWebView 中的 JavaScript 交互及内容区域适配,需通过以下步骤完成:
一、原生与 JavaScript 的交互
1. 原生调用 JavaScript
使用 evaluateJavaScript 方法执行 JS 代码并获取返回值。
示例代码:
// 执行 JS 函数并获取返回值
webView.evaluateJavaScript("getUserName()") { (result, error) in
if let result = result {
print("JS返回的用户名:\(result)")
}
}
2. JavaScript 调用原生
通过注册消息处理器(Message Handler)实现。
步骤:
- 添加消息处理器:
let config = WKWebViewConfiguration()
config.userContentController.add(self, name: "nativeHandler")
webView = WKWebView(frame: .zero, configuration: config)
- 实现
WKScriptMessageHandler协议:
extension ViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "nativeHandler", let body = message.body as? [String: Any] {
print("收到JS消息:\(body)")
// 处理逻辑,如调用原生功能
}
}
}
- JavaScript 发送消息:
// 在 JS 中调用
window.webkit.messageHandlers.nativeHandler.postMessage({ "action": "showToast", "text": "Hello Native!" });
注意事项:
- 内存管理:在控制器销毁时移除消息处理器,避免循环引用。
deinit { webView.configuration.userContentController.removeScriptMessageHandler(forName: "nativeHandler") } - 数据类型:JS 和原生之间传递的数据需为基本类型或 JSON 对象。
二、HTML 内容区域大小适配
1. 动态计算内容高度并调整 WKWebView
在页面加载完成后,通过 JS 获取内容高度并更新布局。
步骤:
- 监听页面加载完成:
extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// 注入 JS 代码获取内容高度
webView.evaluateJavaScript("document.documentElement.scrollHeight") { [weak self] (height, error) in
if let height = height as? CGFloat {
// 调整 WKWebView 的高度约束
self?.webViewHeightConstraint.constant = height
self?.view.layoutIfNeeded()
}
}
}
}
- HTML 中设置视口(Viewport):
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
2. 处理动态内容变化
若内容高度动态变化(如 AJAX 加载),需实时监听并通知原生更新。
示例:
// 在 JS 中监听内容变化,并通知原生
function checkContentHeight() {
const height = document.documentElement.scrollHeight;
window.webkit.messageHandlers.nativeHandler.postMessage({ "type": "heightUpdate", "height": height });
}
// 示例:每隔1秒检测高度变化
setInterval(checkContentHeight, 1000);
原生端处理高度更新:
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if let body = message.body as? [String: Any], body["type"] as? String == "heightUpdate" {
if let height = body["height"] as? CGFloat {
webViewHeightConstraint.constant = height
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
}
}
三、完整代码示例
1. 原生与 JS 交互完整实现
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {
var webView: WKWebView!
var webViewHeightConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// 配置 WKWebView
let config = WKWebViewConfiguration()
config.userContentController.add(self, name: "nativeHandler")
webView = WKWebView(frame: .zero, configuration: config)
webView.navigationDelegate = self
view.addSubview(webView)
// 设置自动布局约束
webView.translatesAutoresizingMaskIntoConstraints = false
webViewHeightConstraint = webView.heightAnchor.constraint(equalToConstant: 200) // 临时测试高度
NSLayoutConstraint.activate([
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) // 添加底部约束
])
// 加载本地 HTML 文件
if let url = Bundle.main.url(forResource: "index", withExtension: "html") {
webView.load(URLRequest(url: url))
}
}
// MARK: - WKScriptMessageHandler
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "nativeHandler", let body = message.body as? [String: Any] {
if let action = body["action"] as? String, action == "showToast" {
let text = body["text"] as? String ?? ""
showToast(text: text)
} else if let height = body["height"] as? CGFloat {
webViewHeightConstraint.constant = height
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
}
}
// MARK: - 原生功能示例:显示 Toast
func showToast(text: String) {
let alert = UIAlertController(title: nil, message: text, preferredStyle: .alert)
present(alert, animated: true) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
alert.dismiss(animated: true)
}
}
}
// MARK: - 内存管理
deinit {
webView.configuration.userContentController.removeScriptMessageHandler(forName: "nativeHandler")
}
}
2. HTML 文件示例(index.html)
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset**=**"UTF-8">
<script>
// JS 调用原生示例
function sendMessageToNative() {
window.webkit.messageHandlers.nativeHandler.postMessage({
"action": "showToast",
"text": "Hello from JavaScript!"
});
}
// 动态内容高度示例
function addContent() {
const div = document.createElement("div");
div.textContent = "New content added!";
document.body.appendChild(div);
// 通知高度变化
const height = document.documentElement.scrollHeight;
window.webkit.messageHandlers.nativeHandler.postMessage({
"type": "heightUpdate",
"height": height
});
}
</script>
</head>
<body>
<h1>WKWebView 交互示例</h1>
<button onclick="sendMessageToNative()">点击调用原生</button>
<button onclick="addContent()">添加内容</button>
</body>
</html>
四、关键注意事项
-
安全性:
- 避免在 JS 中暴露敏感逻辑。
- 对来自 JS 的参数做严格校验,防止注入攻击。
-
性能优化:
- 频繁调用
evaluateJavaScript可能影响性能,建议在必要时使用。 - 动态调整高度时,可添加防抖(Debounce)机制避免频繁布局更新。
- 频繁调用
-
布局适配:
- 使用自动布局约束(Auto Layout)确保 WKWebView 正确响应屏幕旋转。
- 若内容含图片或异步加载资源,需在资源加载完成后重新计算高度。
通过上述方法,可以实现原生与 WKWebView 的高效交互,并确保内容区域自适应显示。