简介
WKWebView
开头的 WK 对应前端每天都在面对的 WebKit1。iOS 8 之后,官方更加提倡使用 WKWebview
来替代 UIWebView
2,因为其在原生和网页交互时,给了原生更多的控制权限和数据交互方式,前者的例子是 HTML 中前端中基本的 alert()
、confirm()
都需要在原生适配了对应的 delegate 之后才能显示,后者对应 prompt()
提交数据也可以直接传送给原生。
新建和加载页面
网页加载进度的显示和 UIProgressView
这一部分首先找到的是资料是 Paul Hudson 写的《Hacking with Swift》 中的第四章《Project 4: Easy Browser》,跟着做了一遍之后得到的效果和录屏的效果相去甚远:例子中的进度条显示并不是在顶部导航栏下有一条进度条,而是在底部的工具栏中。
要实现在顶部进度条(或者还有根据是否 isLoading
来显示「后退」按钮显示与否)的效果,大致的思路是手动建立 UIProgress
和作为前者容器的 Rect
,将 UiProgress
放入 Rect
中,再把 Rect
置于导航栏之下(进度条的粗细通过设置自身的 height
得到)。
其次,进度条中进度的加载,主要概念是 key-value observing (KVO),对前端来说可以想象成 AJAX 上传时候用到的针对 ProgressEvent
的事件监听:
拦截、处理 JavaScript,UIDelegate
WKWebView
中,前端 window 对象里的 alert()
、confirm()
和 prompt()
被前端调用是不会直接显示的,只有在 WKUIDelegate 中进行重写(UIDelegate
里除了三个方法的重写,代码有点长,直接看 demo 源文件里的代码,这里就简单贴一下各个 delegate method 和对应调用的 native 控件):
拦截、并处理 alert()
拦截、并处理 confirm()
拦截、并处理 prompt()
原生调用 JavaScript
和 UIWebView 中对 JavaSCript 的相关执行方法几乎一致,很直觉的 API,直接使用 evaluateJavaScript(_:completionHandler:)
(官方文档)来调用 JavaScript 文件中各个方法就可以。
注入(injecting)一段 JavaScript,进行调用
作为更新和替代,WKWebView
提供了更加丰富的 API,并且对不同种类的 delegate 、方法做了分类。比如:上文提到使用 UIWebView
来注入一段脚本到当前加载的页面中,UIWebView
使用的方法是在 didFinishLoad
的时候使用直接执行脚本;而 WKWebView
则提供了 WKWebViewConfiguration
、WkPerference
。同时,WKWebView
对于加载的时间的控制也可以在 userScript
设置不同的值,比如 injectionTime: .atDocumentEnd
。
关于 WKWebView
的注入,首先需要知道:新建 WKViewView
的方法可以传入第二个参数: WKWebViewConfiguration
对象,这个对象又包含了 userContentController
属性,之后会在 JavaScript 调用中继续使用到这个对象和这个属性,这里先说一下注入脚本用到的:WKUserScript
和 addUserScript(_:)
:
addUserScript(_:)
接收一个 WKUserScript
对象,本地加载页面的过程做一个简单的封装:
录屏中在豆瓣页面打开的时候的就已经变色、假按钮也已经加入,但在操作中还是需要确认网页已经完全加载完毕才能调用具体的方法。
这部分 demo(JavaScriptInvokeNativeViewController.swift
)包含了 JavaScript 调用原生的方法,所以点击按钮会提示 「using window.webkit.messageHandlers.WKMessageSingal」, 具体的是用到了 WKWebview 的 WKScriptMessageHandler
delegate,下面会提到。
JavaScript 调用原生,postMessage 以及 WKScriptMessageHandler
WKWebView
中 JavaScript 调用原生方法和 UIWebView
中的调用一致:原生和 JavaScript 相互之间做约定,不同的是相比 UIWebView
里约定 scheme
那种的方式,WKWebView
提供了单独为和 JavaScript 约定通信而设计的方法和 delegate:WKScriptMessageHandler
(官方文档)3。
「约定方法」和上文说道的「注入脚本」有些类似:通过在新建的时候在 WKWebView
中传入第二个参数 WKWebViewConfiguration
,但参数的传递使用的是 add(_:name:)
官方文档 。整个约定的过程大致分为以下几个步骤:
- 原生(native)声明「暗号」;
- 原生(native)继续声明收到「暗号」、完成数据接收之后做什么;
- JavaScript 发起「暗号」;
- 原生(native)调用约定好使用的方法。
不过到了这里还不能实现调用,还需要重写 WKScriptMessageHandler
中的 userContentController(_:didReceive:)
官方文档 方法:
上面的例子里用 JavaScript 传递给 native 的是一串字符串,还可以直接发送 JSON
,类似:
原生可以使用 Dictionary
来转换:
演示 Demo
Demo 中做了两步:
- 在 iOS 中加载的 html,
input
输入内容后,点击按钮来调用系统组件(弹框); - 通过右上角原生的
Clear Input
按钮来清除 html 中input
里已经输入的内容。
一来一往使 iOS 和 JavaScript 完成了相互调用。
原生(native)声明「暗号(WKMesesgaSignal
)」。
原生(native)继续声明收到「暗号」、完成数据接收之后做什么
原生(native)调用 JavaScript 清除 input
的值
调用 HTML 内的 JavaScript
参考
- WKWebView - NSHipster
- UIWebView和WKWebView的使用及js交互 刘彦玮的技术博客
- Getting started with WKWebView using Swift in iOS 8
- WKWebView and JavaScript in iOS 8 - using Swift
- WKWebView与JS交互,UIWebView+JavascriptCore和JS交互 - Rayshen - 博客园
- 17年跨领域学习 从 WKWebview 再谈混合开发 - 知乎专栏
- iOS H5容器的一些探究(一):UIWebView和WKWebView的比较和选择 - 简书
- Hybrid载体的变化(一)
Which powers Apple’s Safari web browser, It was forked by Google to create Blink ,引自 WebKit 的 维基。 ↩︎
In apps that run in iOS 8 and later, use the WKWebView class instead of using UIWebView ,引自《官方文档》。 ↩︎
可以跟着教程制作一遍,涉及到具体
UIProgressView
的在这一章节的这一部分:《Monitoring page loads: UIToolbar and UIProgressView》。 ↩︎
技术发展迭代很快,所以这些笔记内容也有类似新闻的时效性,不免有过时、或者错误的地方,欢迎指正 ^_^。
BEST
Lien(A.K.A 胡椒)