ios WebView的使用

UIWebView和WKWebView的區(qū)別

UIWebView

加載速度慢、占用內存多,優(yōu)化困難。

研究一下再補充

WKWebView

WKWebView是ios8之后出現(xiàn)的,性能比UIWebView快很多,占用內存少。

特性:
  • 在性能、穩(wěn)定性、功能方面有很大提升。
  • 允許JavaScriptNitro庫加載并使用(UIWebView受限制)
  • 支持了更多的HTML5特性
  • 高達60fps的滾動刷新率以及內置手勢
  • 將UIWebViewDelegate與UIWebView重構成了14類與3個協(xié)議
WKWebView的基本用法:
  1. 加載網(wǎng)頁
  2. 加載的狀態(tài)回調
  3. 新的WKUIDelegate協(xié)議
  4. 動態(tài)加載并運行JS代碼
  5. webview執(zhí)行JS代碼
  6. 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, ̄)/


Potato_zero.jpg
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,631評論 1 32
  • 前言 關于UIWebView的介紹,相信看過上文的小伙伴們,已經(jīng)大概清楚了吧,如果有問題,歡迎提問。 本文是本系列...
    CoderLF閱讀 9,308評論 2 12
  • 一、WKWebView簡介 UIWebView自iOS2就有,WKWebView從iOS8才有,毫無疑問WKWeb...
    慌莫染閱讀 4,197評論 0 4
  • 一. 創(chuàng)建: 二. 加載方式: 常用的網(wǎng)頁加載方式 加載html格式的內容(html文件中加載和html格式字...
    fjytqiu閱讀 4,997評論 0 10
  • 前言: web頁面和app的直接的交互是很常見的東西,在ios8之前,用的是uiwebview,但是在ios8之后...
    qingchen91閱讀 3,074評論 6 25

友情鏈接更多精彩內容