WKWebView 概述
WKWebView 是 Apple 提供的现代 Web 内容渲染控件,替代了早期的 UIWebView。它基于 WebKit 框架,提供更高的性能、更低的内存占用以及更丰富的功能,适用于 iOS 和 macOS 应用。
基本用法
WKWebView 的核心功能是加载和显示网页内容。以下是一个简单的初始化示例:
import WebKit
let webView = WKWebView(frame: view.bounds)
view.addSubview(webView)
if let url = URL(string: "https://www.apple.com") {
let request = URLRequest(url: url)
webView.load(request)
}
配置 WKWebView
可以通过 WKWebViewConfiguration
自定义行为,例如启用 JavaScript 或设置内容控制器:
let configuration = WKWebViewConfiguration()
configuration.allowsInlineMediaPlayback = true
configuration.preferences.javaScriptEnabled = true
let webView = WKWebView(frame: .zero, configuration: configuration)
处理导航和交互
实现 WKNavigationDelegate
可以监听页面加载状态:
class ViewController: UIViewController, WKNavigationDelegate {
override func viewDidLoad() {
super.viewDidLoad()
webView.navigationDelegate = self
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("页面加载完成")
}
}
JavaScript 交互
通过 WKUserContentController
实现原生与 JavaScript 的双向通信:
configuration.userContentController.add(self, name: "nativeHandler")
// 从 JavaScript 接收消息
extension ViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "nativeHandler" {
print("收到 JS 消息: \(message.body)")
}
}
}
缓存与 Cookie 管理
使用 WKWebsiteDataStore
管理缓存和 Cookie:
let dataStore = WKWebsiteDataStore.default()
dataStore.fetchDataRecords(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes()) { records in
for record in records {
print("缓存记录: \(record.displayName)")
}
}
性能优化
WKWebView 默认已优化性能,但可通过以下方式进一步调整:
- 禁用不必要的特性(如自动播放)
- 使用
evaluateJavaScript(_:completionHandler:)
替代频繁的 JS 调用 - 预加载页面或资源
WKNavigationDelegate
处理网页导航相关事件,例如页面加载、重定向或错误。
// 页面开始加载时触发
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {}
// 页面加载完成时触发
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {}
// 页面加载失败时触发
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {}
// 决定是否允许导航请求(例如点击链接或提交表单)
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
decisionHandler(.allow) // 或 .cancel
}
WKUIDelegate
处理网页的 UI 相关交互,例如 JavaScript 弹窗或新窗口打开。
// 处理 JavaScript 的 alert 弹窗
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true)
completionHandler()
}
// 处理 JavaScript 的 confirm 弹窗
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in completionHandler(false) }))
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in completionHandler(true) }))
present(alert, animated: true)
}
WKScriptMessageHandler
用于与网页中的 JavaScript 进行双向通信。
// 注册消息处理器
let userContentController = WKUserContentController()
userContentController.add(self, name: "nativeHandler")
// 接收来自 JavaScript 的消息
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "nativeHandler" {
print("Received message: \(message.body)")
}
}
注意事项
- WKWebView 与 UIWebView 的 API 不兼容
- 跨域请求限制更严格,需服务器配合 CORS
- 部分旧版 HTML5 特性可能不支持
- 内存管理:WKWebView 的 delegate 需避免循环引用,建议使用
weak
引用。 - 线程安全:WKWebView 的回调方法可能在非主线程执行,UI 操作需切换到主线程。
- HTTP 认证:通过
didReceive challenge
方法处理 HTTPS 认证请求。
// HTTPS 认证处理
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
completionHandler(.useCredential, credential)
}
}