UIWebView和WKWebView的區(qū)別
UIWebView
加載速度慢、占用內存多,優(yōu)化困難。
研究一下再補充
WKWebView
WKWebView是ios8之后出現(xiàn)的,性能比UIWebView快很多,占用內存少。
特性:
- 在性能、穩(wěn)定性、功能方面有很大提升。
- 允許JavaScriptNitro庫加載并使用(UIWebView受限制)
- 支持了更多的HTML5特性
- 高達60fps的滾動刷新率以及內置手勢
- 將UIWebViewDelegate與UIWebView重構成了14類與3個協(xié)議
WKWebView的基本用法:
- 加載網(wǎng)頁
- 加載的狀態(tài)回調
- 新的WKUIDelegate協(xié)議
- 動態(tài)加載并運行JS代碼
- webview執(zhí)行JS代碼
- JS調用App注冊過的方法
WKWebView相關類
類(使用中發(fā)現(xiàn)的)
- WKWebView
- WKUserScript
- WKWebViewConfiguration
- WKUserContentController
- WKScriptMessage
協(xié)議(使用中發(fā)現(xiàn)的)
- WKUIDelegate
- WKNavigationDelegate
- WKScriptMessageHandler
一、加載網(wǎng)頁
加載網(wǎng)頁或者HTML代碼的方式和UIWebView相同
wkwebview = WKWebView()
let url = URL(string: urlStr)
let request = URLRequest(url: url!)
wkwebview.load(request)
二、加載的狀態(tài)回調 (WKNavigationDelegate)
用來追蹤加載過程(頁面開始加載、加載完成、加載失?。┑姆椒ǎ?/p>
// 頁面開始加載時調用
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
print("頁面開始加載時調用")
}
// 頁面內容開始返回調用
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
print("頁面內容開始返回調用")
}
// 頁面加載完成之后調用
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("頁面加載完成之后調用")
}
// 頁面加載失敗時調用
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
print("頁面加載失敗時調用")
}
頁面跳轉的代理方法:
// 在發(fā)送請求之前,決定是否跳轉
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
print("在發(fā)送請求之前,決定是否跳轉")
decisionHandler(.allow)
}
// 在收到響應后,決定是否跳轉
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
print("在收到響應后,決定是否跳轉")
decisionHandler(.allow)
}
// 接收到服務器跳轉請求之后執(zhí)行
func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {
print("接收到服務器跳轉請求之后執(zhí)行")
}
三、WKUIDelegate協(xié)議
這個協(xié)議主要用于WKWebView處理web界面的三種提示框(警告框、確認框、輸入框),下面是警告框的例子:
/**
* web界面中有彈出警告框時使用
*
* @param webview 實現(xiàn)代理webview
* @param message 警告框中的內容
* @param frame 主窗口
* @param completionHandler 警告框消失調用
*/
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
print("界面彈出了警告框")
print("警告框的內容:\(message)")
completionHandler()
}
四、動態(tài)加載JS
用于在客戶端內部加入JS代碼,添加js_name的點擊事件
let jsStr = "var js_name = document.getElementById('js_name');function haha() {window.alert(js_name.text)};js_name.addEventListener('click', haha, false);"
let script: WKUserScript = WKUserScript(source: jsStr, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
let configuration = WKWebViewConfiguration()
configuration.userContentController.addUserScript(script)
wkwebview = WKWebView(frame: CGRect.zero, configuration: configuration)
wkwebview.uiDelegate = self
wkwebview.navigationDelegate = self
let url = URL(string: urlStr)
let request = URLRequest(url: url!)
wkwebview.load(request)
wkwebview.allowsBackForwardNavigationGestures = true
self.view.addSubview(wkwebview)
五、webView執(zhí)行JS代碼
執(zhí)行已經(jīng)動態(tài)添加JS的haha()方法,原有的js方法也可以調用
// wkwebview執(zhí)行JS代碼
wkwebview.evaluateJavaScript("haha()") { (_, _) in
print("完成執(zhí)行js方法的回調")
}
六、JS調用App注冊的方法
首先,WKWebView里面注冊JS需要調用的方法,是通過WKUserContentController類下面的方法
open func add(_ scriptMessageHandler: WKScriptMessageHandler, name: String)
scriptMessageHandler是代理回調,JS調用name方法后,會回調WKScriptMessageHandler代理里的這個方法
public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)
message包含著請求的方法的名稱和參數(shù),message.name是調用的方法名稱,message.body是調用的方法的參數(shù)
然后JS在調用方法的時候要用下面的方法
window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
注意,name(方法名)是放在中間的,messageBody只能是一個對象,如果要傳多個值,需要封裝成數(shù)組,或者字典。
實例代碼(關鍵代碼)
func initWKWebView() {
self.view.backgroundColor = UIColor.white
// 動態(tài)添加JS代碼,實現(xiàn)某個控件的點擊事件
let jsStr = "var stundet = {'name': 'potato'};var js_name = document.getElementById('js_name');function haha() {window.webkit.messageHandlers.testJS.postMessage(stundet);};js_name.addEventListener('click', haha, false);"
let script: WKUserScript = WKUserScript(source: jsStr, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
let configuration = WKWebViewConfiguration()
configuration.userContentController.addUserScript(script)
wkwebview = WKWebView(frame: CGRect.zero, configuration: configuration)
wkwebview.uiDelegate = self
wkwebview.navigationDelegate = self
let url = URL(string: urlStr)
let request = URLRequest(url: url!)
wkwebview.load(request)
wkwebview.allowsBackForwardNavigationGestures = true
self.view.addSubview(wkwebview)
wkwebview.snp.makeConstraints { (make) in
make.top.equalTo(statusBarHeight + navigationBarHeight)
make.left.right.bottom.equalToSuperview()
}
// 注冊供JS調用的方法
wkwebview.configuration.userContentController.add(self, name: "testJS")
}
func testJS(name: String) {
print("js調用原生方法, 參數(shù)name:\(name)")
}
// 實現(xiàn)代理
extension WebViewViewController: WKScriptMessageHandler {
// JS調用原生方法的處理
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print("JS 調用了\(message.name)方法,傳回參數(shù)\(message.body)")
if message.name == "testJS" {
let data: Dictionary = message.body as! Dictionary<String, Any>
testJS(name:data["name"] as! String)
}
}
}
??使用了WKScriptMessageHandler后發(fā)現(xiàn)會造成內存泄漏,當前頁面沒有銷毀,deinit方法執(zhí)行。
七、解決JS調用APP注冊的方法造成的內存泄漏
內存泄漏主要是因為循環(huán)引用造成self無法銷毀,于是定義一個弱引用的WKScriptMessageHandler
import UIKit
import WebKit
class WeakScriptMessageDelegate: NSObject, WKScriptMessageHandler {
// 弱引用
weak var delegate: WKScriptMessageHandler?
init(_ delegate: WKScriptMessageHandler) {
super.init()
self.delegate = delegate
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
self.delegate?.userContentController(userContentController, didReceive: message)
}
}
// 擴展WKUserContentController
extension WKUserContentController {
func addHandler(_ message: Any, name: String) {
if let msg = message as? WKScriptMessageHandler {
self.add(WeakScriptMessageDelegate(msg), name: name)
}
}
}
把原來注冊供JS調用的方法代碼改成
wkwebview.configuration.userContentController.add(WeakScriptMessageDelegate(self), name: "testJS")
或者
使用擴展添加的方法
wkwebview.configuration.userContentController.addHandler(self, name: "testJS")
在deinit中記得remove掉注冊的方法
deinit {
wkwebview.configuration.userContentController.removeScriptMessageHandler(forName: "testJS")
print("WebViewViewController 銷毀")
}
參考博客 - 感謝大神的貢獻
好好學習,天天向上。<( ̄oo, ̄)/
