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从入门到精通》


目录
相关文章
|
12天前
|
Web App开发 JavaScript 前端开发
如何确保 Math 对象的方法在不同的 JavaScript 环境中具有一致的精度?
【10月更文挑战第29天】通过遵循标准和最佳实践、采用固定精度计算、进行全面的测试与验证、避免隐式类型转换以及持续关注和更新等方法,可以在很大程度上确保Math对象的方法在不同的JavaScript环境中具有一致的精度,从而提高代码的可靠性和可移植性。
|
24天前
|
缓存 监控 前端开发
JavaScript 实现大文件上传的方法
【10月更文挑战第17天】通过以上步骤和方法,我们可以实现较为可靠和高效的大文件上传功能。当然,具体的实现方式还需要根据实际的应用场景和服务器要求进行调整和优化。
|
11天前
|
JavaScript 前端开发 索引
js中DOM的基础方法
【10月更文挑战第31天】这些DOM基础方法是操作网页文档结构和实现交互效果的重要工具,通过它们可以动态地改变页面的内容、样式和行为,为用户提供丰富的交互体验。
|
11天前
|
缓存 JavaScript UED
js中BOM中的方法
【10月更文挑战第31天】
|
11天前
|
JavaScript 前端开发
.js方法参数argument
【10月更文挑战第26天】`arguments` 对象为JavaScript函数提供了一种灵活处理参数的方式,能够满足各种不同的参数传递和处理需求,在实际开发中具有广泛的应用价值。
27 7
|
12天前
|
设计模式 前端开发 JavaScript
揭秘!前端大牛们如何巧妙利用JavaScript,打造智能交互体验!
【10月更文挑战第30天】前端开发领域充满了无限可能与创意,JavaScript作为核心语言,凭借强大的功能和灵活性,成为打造智能交互体验的重要工具。本文介绍前端大牛如何利用JavaScript实现平滑滚动、复杂动画、实时数据更新和智能表单验证等效果,展示了JavaScript的多样性和强大能力。
30 4
|
10天前
|
移动开发 前端开发 JavaScript
前端实训,刚入门,我用原生技术(H5、C3、JS、JQ)手写【网易游戏】页面特效
于辰在大学期间带领团队参考网易游戏官网的部分游戏页面,开发了一系列前端实训作品。项目包括首页、2021校园招聘页面和明日之后游戏页面,涉及多种特效实现,如动态图片切换和人物聚合效果。作品源码已上传至CSDN,视频效果可在CSDN预览。
17 0
前端实训,刚入门,我用原生技术(H5、C3、JS、JQ)手写【网易游戏】页面特效
|
12天前
|
JavaScript 前端开发 图形学
JavaScript 中 Math 对象常用方法
【10月更文挑战第29天】JavaScript中的Math对象提供了丰富多样的数学方法,涵盖了基本数学运算、幂运算、开方、随机数生成、极值获取以及三角函数等多个方面,为各种数学相关的计算和处理提供了强大的支持,是JavaScript编程中不可或缺的一部分。
|
17天前
|
JavaScript 前端开发 Go
异步加载 JS 的方法
【10月更文挑战第24天】异步加载 JavaScript 是提高网页性能和用户体验的重要手段。通过使用不同的方法和技术,可以实现灵活、高效的异步加载 JavaScript。在实际应用中,需要根据具体情况选择合适的方法,并注意处理可能出现的问题,以确保网页能够正常加载和执行。
|
28天前
|
人工智能 JavaScript 网络安全
ToB项目身份认证AD集成(三完):利用ldap.js实现与windows AD对接实现用户搜索、认证、密码修改等功能 - 以及针对中文转义问题的补丁方法
本文详细介绍了如何使用 `ldapjs` 库在 Node.js 中实现与 Windows AD 的交互,包括用户搜索、身份验证、密码修改和重置等功能。通过创建 `LdapService` 类,提供了与 AD 服务器通信的完整解决方案,同时解决了中文字段在 LDAP 操作中被转义的问题。