简介
WKWebView 开头的 WK 对应前端每天都在面对的 WebKit1。iOS 8 之后,官方更加提倡使用 WKWebview 来替代 UIWebView2,因为其在原生和网页交互时,给了原生更多的控制权限和数据交互方式,前者的例子是 HTML 中前端中基本的 alert()、confirm() 都需要在原生适配了对应的 delegate 之后才能显示,后者对应 prompt() 提交数据也可以直接传送给原生。
新建和加载页面

网页加载进度的显示和 UIProgressView
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()
UIAlertController,perferredStyle 为 .alert。拦截、并处理 confirm()
UIAlertController,perferredStyle 似乎 .alert 和 .actionSheet 都可以。拦截、并处理 prompt()
对应 native 里的 UIAlertController,perferredStyle 为 .alert。
需要调用 addTextField(configurationHandler:) 方法来处理用户输入。
原生调用 JavaScript
和 UIWebView 中对 JavaSCript 的相关执行方法几乎一致,很直觉的 API,直接使用 evaluateJavaScript(_:completionHandler:)(官方文档)来调用 JavaScript 文件中各个方法就可以。
调用的方法:
调用的方法:
调用的方法:
注入(injecting)一段 JavaScript,进行调用
作为更新和替代,WKWebView 提供了更加丰富的 API,并且对不同种类的 delegate 、方法做了分类。比如:上文提到使用 UIWebView 来注入一段脚本到当前加载的页面中,UIWebView 使用的方法是在 didFinishLoad 的时候使用直接执行脚本;而 WKWebView 则提供了 WKWebViewConfiguration、WkPerference。同时,WKWebView 对于加载的时间的控制也可以在 userScript 设置不同的值,比如 injectionTime: .atDocumentEnd。
WKWebview 注入了上面提到的 user_script.js,user_script.js 中包含两个方法,改变网页色的 changePageBackgroundColor() 和在首页四个按钮下加入一个新按钮的 addFakeSignalButton()。关于 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)调用约定好使用的方法。
WKMesesgaSignal 就是约定的「暗号」,实际上所做的是:当前 WKWebView 加载完毕了 HTML 之后,前端再熟悉不过的 window 对象会有多一个属性 WKMesesgaSignal 来让前端 postMesage()。不过到了这里还不能实现调用,还需要重写 WKScriptMessageHandler 中的 userContentController(_:didReceive:) 官方文档 方法:
上面的例子里用 JavaScript 传递给 native 的是一串字符串,还可以直接发送 JSON,类似:
原生可以使用 Dictionary 来转换:
演示 Demo
Demo 中做了两步:
- 在 iOS 中加载的 html,input输入内容后,点击按钮来调用系统组件(弹框);
- 通过右上角原生的 Clear Input按钮来清除 html 中input里已经输入的内容。
一来一往使 iOS 和 JavaScript 完成了相互调用。
原生(native)声明「暗号(WKMesesgaSignal)」。
WKMesesgaSignal)」原生(native)继续声明收到「暗号」、完成数据接收之后做什么
WKScriptMessageHandler delegate 中执行,类似网页中 DOM.ready()。原生(native)调用 JavaScript 清除 input 的值
调用 HTML 内的 JavaScript
input 值传递给原生,使用 WKWebViewConfiguration 对象和 WKScriptMessageHandler 方法。参考
- 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 胡椒)
