美文网首页
ios WebView的使用

ios WebView的使用

作者: 欢乐的乐 | 来源:发表于2018-12-21 13:48 被阅读134次

UIWebView和WKWebView的区别

UIWebView

加载速度慢、占用内存多,优化困难。

研究一下再补充

WKWebView

WKWebView是ios8之后出现的,性能比UIWebView快很多,占用内存少。

特性:
  • 在性能、稳定性、功能方面有很大提升。
  • 允许JavaScriptNitro库加载并使用(UIWebView受限制)
  • 支持了更多的HTML5特性
  • 高达60fps的滚动刷新率以及内置手势
  • 将UIWebViewDelegate与UIWebView重构成了14类与3个协议
WKWebView的基本用法:
  1. 加载网页
  2. 加载的状态回调
  3. 新的WKUIDelegate协议
  4. 动态加载并运行JS代码
  5. webview执行JS代码
  6. JS调用App注册过的方法
WKWebView相关类
类(使用中发现的)
  • WKWebView
  • WKUserScript
  • WKWebViewConfiguration
  • WKUserContentController
  • WKScriptMessage
协议(使用中发现的)
  • WKUIDelegate
  • WKNavigationDelegate
  • WKScriptMessageHandler

一、加载网页

加载网页或者HTML代码的方式和UIWebView相同

wkwebview = WKWebView()
let url = URL(string: urlStr)
let request = URLRequest(url: url!)
wkwebview.load(request)

二、加载的状态回调 (WKNavigationDelegate)

用来追踪加载过程(页面开始加载、加载完成、加载失败)的方法:

// 页面开始加载时调用
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("页面加载失败时调用")
}

页面跳转的代理方法:

// 在发送请求之前,决定是否跳转
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    print("在发送请求之前,决定是否跳转")
    decisionHandler(.allow)
}

// 在收到响应后,决定是否跳转
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
    print("在收到响应后,决定是否跳转")
    decisionHandler(.allow)
}

// 接收到服务器跳转请求之后执行
func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {
    print("接收到服务器跳转请求之后执行")
}

三、WKUIDelegate协议

这个协议主要用于WKWebView处理web界面的三种提示框(警告框、确认框、输入框),下面是警告框的例子:

/**
 *  web界面中有弹出警告框时使用
 *
 *  @param webview              实现代理webview
 *  @param message              警告框中的内容
 *  @param frame                主窗口
 *  @param completionHandler    警告框消失调用
 */
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
    print("界面弹出了警告框")
    print("警告框的内容:\(message)")
    completionHandler()
}

四、动态加载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执行JS代码

执行已经动态添加JS的haha()方法,原有的js方法也可以调用

// wkwebview执行JS代码
wkwebview.evaluateJavaScript("haha()") { (_, _) in
    print("完成执行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包含着请求的方法的名称和参数,message.name是调用的方法名称,message.body是调用的方法的参数

然后JS在调用方法的时候要用下面的方法

window.webkit.messageHandlers.<name>.postMessage(<messageBody>)

注意,name(方法名)是放在中间的,messageBody只能是一个对象,如果要传多个值,需要封装成数组,或者字典。

实例代码(关键代码)
func initWKWebView() {
    self.view.backgroundColor = UIColor.white
    
    // 动态添加JS代码,实现某个控件的点击事件
    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调用原生方法, 参数name:\(name)")
}

// 实现代理
extension WebViewViewController: WKScriptMessageHandler {
    // JS调用原生方法的处理
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        print("JS 调用了\(message.name)方法,传回参数\(message.body)")
        if message.name == "testJS" {
            let data: Dictionary = message.body as! Dictionary<String, Any>
            testJS(name:data["name"] as! String)
        }
        
    }
}

⚠️使用了WKScriptMessageHandler后发现会造成内存泄漏,当前页面没有销毁,deinit方法执行。

七、解决JS调用APP注册的方法造成的内存泄漏

内存泄漏主要是因为循环引用造成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

相关文章

网友评论

      本文标题:ios WebView的使用

      本文链接:https://www.haomeiwen.com/subject/bppmkqtx.html