WKWebView 原生与JavaScript交互方法

简介: WKWebView 原生与JavaScript交互方法

认识WebKit框架



对于简单的网页嵌入需求,使用UIWebView已经足够,但是对于更加复杂的需求,例如在网页与原生交互十分频繁的场景中,UIWebView处理起来就有些力不从心。iOS8之后,系统引入了WebKit框架,将应用中嵌入网页的相关接口提取整合成了一个完整的框架,WebKit框架中添加了一些原生与JavaScript交互的方法,并且WebKit框架中采用导航栈的模型进行网页的跳转管理,开发者可以更加容易地控制和管理网页的渲染。


WebKit框架中设计的类很多,框架的设计十分面向对象化和模块化,这也更有利于开发者代码的结构化。如图14-29中描述了WebKit框架中所涉及的重要的类和其相互之间的关系。


2466108-de2e7603b85af1fb.webp.jpg


图14-29 WebKit框架结构图


WebKit框架中最核心的类应该属于WKWebView类,这个类专门用来进行网页视图的渲染,其需要通过WKWebViewConfiguration类来进行配置。


而WKWebViewConfiguration类中主要包含3个模块:WKPreferences用于进行自定义的偏好配置,WKProcesssPool类用于进程池的配置,WKUserContentController类也是一个核心类,其主要用来做native与JavaScript的交互管理。


关于WKUserContentController,其又需要两个类的协作,WKUserScript类用于通过原生代码向网页注入JavaScript代码,WKScriptMessageHandler类用于处理JavaScript代码调用的原生方法。除了前面提到的这些类外,开发者可以通过WKNavigationDelegate协议监听网页的活动,WKNavigationAction类是某个活动的实例化对象,


WKUIDelegate协议用于处理JavaScript代码中的一些弹出框事件,WKBackForwardList类用于栈结构的管理,WKBackForwardListItem类是每个网页节点的实例化对象。


使用WKWebViewConfiguration对网页视图进行配置



上一节整体向读者介绍了WebKit框架的结构和组成,整体分析清楚这个框架并不是一件容易的事。读者可以采取由点到线、由线到面的学习过程来掌握WebKit框架。首先WebKit框架中网页的展示和渲染是通过WKWebView来完成的,因此,可以从WKWebView入手学习,WKWebView的配置所依赖的类又是WKWebViewConfiguration类,因此最先要学习的是如何通过WKUserContentController对网页视图进行配置,并了解通过配置可以完成怎样的功能。使用Xcode开发工具创建一个命名为WebKitTwst的工程,要支持Http协议的网络请求,根据前面介绍的过程向Info.plist文件中添加相应键值。首先在ViewController.swift文件中引入WebKit框架,如下所示:


import WebKit


在ViewController类中添加如下代码,使界面加载出百度网站首页。


//声明WKWebView属性
var wkView:WKWebView?
override func viewDidLoad() {
    super.viewDidLoad()
    //创建网页配置
    let configuration = WKWebViewConfiguration()
    //对网页视图进行实例化
    wkView = WKWebView(frame: self.view.frame, configuration: configuration)
    self.view.addSubview(wkView!)
    let url = URL(string: "http://www.baidu.com")
    let request = URLRequest(url: url!)
    wkView!.load(request) 
}


运行工程,可以看到使用WKWebView将百度首页加载了出来。可以使用WKProcessPool类为WKWebView实例配置一个进程池,示例如下:


//创建网页配置
let configuration = WKWebViewConfiguration()
//配置进程池
let processPool = WKProcessPool()
configuration.processPool = processPool


开发者一般不需要对进程池进行配置,配置为同一进程池中的WKWebView实例会共享一些资源,进程池在需要使用多个WKWebView实例时会使用到。对WKWebView实例进行自定义的偏好配置示例如下:


//偏好配置
let prefrence = WKPreferences()
//设置网页界面的最小字号
prefrence.minimumFontSize = 0
//设置是否允许不经过用户交互由JavaScript代码自动打开窗口
prefrence.javaScriptCanOpenWindowsAutomatically = true
configuration.preferences = prefrence
//设置是否支持JavaScript脚本 默认为true
configuration.defaultWebpagePreferences.allowsContentJavaScript = true


上面的示例代码中,可将WKPreferences类实例的javaScriptCanOpenWindowsAutomaticlly属性简单理解为是否允许自动弹出网页。


使用WKUserContentController类来进行原生与JavaScript交互相关配置,示例代码如下:


//进行原生与JavaScript交互配置
let userContentController = WKUserContentController()
//设置代理并且注册要被javaScript代码调用的原生方法名称
userContentController.add(self, name: "nativeFunc")
//向网页中注入一段javaScript代码
let javaScriptString = "function userFunc(){window.webkit.messageHandlers.nativeFunc.postMessage({\"班级\":\"珲少学堂\"})}; userFunc()"
let userScript = WKUserScript(source: javaScriptString, injectionTime: .atDocumentStart, forMainFrameOnly: false)
//进行注入
userContentController.addUserScript(userScript)
configuration.userContentController = userContentController


WKUserContentController实例调用add(_: , name:)方法用于设置JavaScript方法回调的代理和要监听的方法名。上面代码可以理解为,在WebKit中注册了nativeFunc方法的监听,当JavaScript代码对nativeFunc进行调用时,就会通知到WKScriptMessageHandler协议中约定的方法,开发者可进行后续的逻辑处理。


WKUserContentController实例调用addUserScript()方法用于向网页中注入JavaScript代码,其中注入的JavaScript代码是封装入WKUserScript的对象,WKUserScript类的构造方法WKUserScript(source: , injecttTime: ,forMainFrameOnly:)第1个参数为注入的JavaScript代码字符串,第2个参数设置注入的时机,第3个参数设置是否只在主界面注入,关于可注入时机定义在如下枚举中:


public enum WKUserScriptInjectionTime : Int {
    //在文档首部进行注入
    case atDocumentStart = 0
    //在文档尾部进行注入
    case atDocumentEnd = 1
}


上面示例代码中注入的JavaScript代码如下:


function userFunc(){
    window.webkit.messageHandlers.nativeFunc.postMessage({\"班级\":\"珲少学堂\"})
}; 
userFunc()


这段JavaScript代码的含义是创建了一个函数userFunc(),函数中向nativeFunc方法发送了一个字典消息,字典中的键为“班级”,值为“珲少学堂”,之后调用了userFunc()这个方法。因此读者可以理解为,这里手动向JavaScript注入了代码,代码的功能是与原生nativeFunc方法进行通信。


为ViewController类添加如下协议:


class ViewController: UIViewController, WKScriptMessageHandler


实现协议中的方法如下:


func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    print(message.body, message.name)
}


当JavaScript代码向WebKit中注册的交互方法发送消息后,系统会调用这个协议方法,这个方法会传入一个WKScriptMessage类型的message参数,其中封装了传递的消息内容,WKScriptMessage类中提供的重要属性如下:


open class WKScriptMessage : NSObject {
    //消息体 对应上面传递的字典
    open var body: Any { get }
    //此消息的网页视图来源
    weak open var webView: WKWebView? { get }
    //传递消息的方法名
    open var name: String { get }
}


开发者也可以使用原生代码来调用网页中的JavaScript方法,使用WKWebView类中的如下方法:


public func evaluateJavaScript(_ javaScriptString: String, completionHandler:((AnyObject?, NSError? ) -> Swift.Void)? = nil)


WKWebView中重要属性和方法解析



WKWebView明显比UIWebView要强大许多,其中除了支持UIWebView中所有的方法外,还扩展添加了更多强大实用的功能。关于网页的前进和后退操作,WKWebView中提供了backForwardList属性,如下:


public var backForwordList: WKBackForwardList{ get }


WKBackForwardList类中存储了网页跳转中的各个网页节点,其中属性列举如下:


//当前的网页节点
var currentItem: WKBackForwardListItem? { get }
//前进一个的网页节点
var backItem: WKBackForwardListItem? { get }
//后退一个的网页节点
open var forwardItem: WKBackForwardListItem? { get }
//某个索引对应的网页节点
open func item(at index: Int) -> WKBackForwardListItem?
//所有后退的网页节点组成的数组
open var backList: [WKBackForwardListItem] { get }
//所有前进的网页节点组成的数组
open var forwardList: [WKBackForwardListItem] { get }


通过WKBackForwardList类,开发者可以方便地获取到在整个网页跳转过程中缓存的任意一个网页节点。WKWebView类实例调用如下的方法可以跳转到任意一个网页节点:


public func go(to item: WKBackForwardListItem) -> WKNavigation?


WKWebKit类中还有一个十分重要的属性是estimatedProgress属性,这个属性是一个Double类型的值,其代表了网页加载的进度。在许多浏览器中,都会有一个网页加载进度条,用户可以通过进度条得知当前网页的加载状态。在实际开发中,配合UIProgressView进度条控件与KVO技术,就可以实现网页加载进度条功能。在ViewController类中添加UIProgressView属性如下:


var progressView:UIProgressView?


在ViewController类的viewDidLoad()方法中添加如下代码:


//创建进度条控件
progressView = UIProgressView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 10))
progressView? .progressTintColor = UIColor.green
progressView? .progress = 0
self.view.addSubview(progressView!)
//对WKWebView实例的estimatedProgress属性进行监听
wkView? .addObserver(self, forKeyPath: "estimatedProgress", options: NSKeyValueObservingOptions.new, context: nil)
//在ViewController类中实现如下方法
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "estimatiedProgress" {
            progressView? .progress = Float(wkView! .estimatedProgress)
    }
}


KVO全称Key-Value-Observe,即键值监听。是iOS开发中一种常用的属性监听技术,其通过设置监听者来监听某个类的某个属性,当属性值发生改变时就会通知监听者,在回调的通知方法中,开发者可以获取到所监听的属性值的相关信息。


关于WKUIDelegate协议



WKUIDelegate协议也是处理JavaScript与原生代码交互的一个重要协议,其中主要定义了一些与网页警告框有关的回调方法。重要方法列举如下:


//当网页视图被创建时会调用
optional func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView?
//当网页视图被关闭时会被调用
optional func webViewDidClose(_ webView: WKWebView)
//JavaScript代码弹出alert()警告框时会调用的原生方法 开发者处理完成逻辑后,需要调用completionHandler()闭包进行返回
optional func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void)
//JavaScript代码弹出confirm()确认框的时候会调用的原生方法 开发者处理完成逻辑后,需要调用completionHandler()闭包,这个闭包中需要传入一个Bool类型的参数将用户选择的结果返回
optional func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void)
//  JavaScript的prompt事件回调的原生方法prompt会在网页中弹出输入框,开发者处理完交互逻辑后,需要调用completionHandler()闭包将用户输入的信息回传  
optional func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void)


WebKit框架是iOS开发框架中一个比较复杂的框架,也是难点与进阶点。读者在学习的时候,注意整体把握,在实战练习中多多理解体会。


摘自《Swift从入门到精通》


目录
相关文章
|
5月前
|
监控 负载均衡 JavaScript
有哪些有效的方法可以优化Node.js应用的性能?
有哪些有效的方法可以优化Node.js应用的性能?
301 69
|
4月前
|
JavaScript Linux 内存技术
Debian 11系统下Node.js版本更新方法详解
本指南详细介绍在Linux系统中安装和管理Node.js的步骤。首先检查现有环境,包括查看当前版本和清除旧版本;接着通过NodeSource仓库安装最新版Node.js并验证安装结果。推荐使用nvm(Node Version Manager)进行多版本管理,便于切换和设置默认版本。同时,提供常见问题解决方法,如权限错误处理和全局模块迁移方案,以及版本回滚操作,确保用户能够灵活应对不同需求。
326 0
|
4月前
|
JavaScript Linux 内存技术
Debian 11系统下Node.js版本更新方法
Debian 11更新Node.js主要就是这三种方式,无论你是初涉其中的新手还是找寻挑战的专家,总有一种方式能满足你的需求。现在,你已经是这个
345 80
|
8月前
|
前端开发 JavaScript
有没有方法可以保证在JavaScript中多个异步操作的执行顺序?
有没有方法可以保证在JavaScript中多个异步操作的执行顺序?
311 58
|
6月前
|
JavaScript 前端开发 Java
js 垃圾回收机制的方法
JS回收机制方法讲解
|
7月前
|
JavaScript 前端开发 Java
深入理解 JavaScript 中的 Array.find() 方法:原理、性能优势与实用案例详解
Array.find() 是 JavaScript 数组方法中一个非常实用和强大的工具。它不仅提供了简洁的查找操作,还具有性能上的独特优势:返回的引用能够直接影响原数组的数据内容,使得数据更新更加高效。通过各种场景的展示,我们可以看到 Array.find() 在更新、条件查找和嵌套结构查找等场景中的广泛应用。 在实际开发中,掌握 Array.find() 的特性和使用技巧,可以让代码更加简洁高效,特别是在需要直接修改原数据内容的情形。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一
|
7月前
|
移动开发 运维 供应链
通过array.some()实现权限检查、表单验证、库存管理、内容审查和数据处理;js数组元素检查的方法,some()的使用详解,array.some与array.every的区别(附实际应用代码)
array.some()可以用来权限检查、表单验证、库存管理、内容审查和数据处理等数据校验工作,核心在于利用其短路机制,速度更快,节约性能。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
7月前
|
供应链 JavaScript 前端开发
通过array.every()实现数据验证、权限检查和一致性检查;js数组元素检查的方法,every()的使用详解,array.some与array.every的区别(附实际应用代码)
array.every()可以用来数据验证、权限检查、一致性检查等数据校验工作,核心在于利用其短路机制,速度更快,节约性能。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
9月前
|
JavaScript 前端开发 测试技术
盘点原生JavaScript中直接触发事件的方式
本文全面探讨了原生JavaScript中触发事件的多种方式,包括`dispatchEvent`、`Event`构造函数、`CustomEvent`构造器、直接调用事件处理器以及过时的`createEvent`和`initEvent`方法。通过技术案例分析,如模拟点击事件、派发自定义数据加载事件和实现提示框系统,帮助开发者掌握这些方法在实际开发中的应用,提升灵活性与兼容性。
282 3
|
9月前
|
JavaScript 前端开发 开发者
JavaScript字符串的常用方法
在JavaScript中,字符串处理是一个非常常见的任务。JavaScript提供了丰富的字符串操作方法,使开发者能够高效地处理和操作字符串。本文将详细介绍JavaScript字符串的常用方法,并提供示例代码以便更好地理解和应用这些方法。
159 13

热门文章

最新文章