在应用的一些不重要的界面(比如功能的使用说明页面)上使用 HTML 进行开发可以在不降低用户体验的基础上,加快开发效率。
这篇文章尝试介绍一些这方面的实践。
# 加载本地HTML
我们在本地创建好一个名为 index.html 的文件后,将文件拖入到 iOS 项目的 Xcode 工程中。
iOS 加载本地 HTML 的方式如下:
let wkconfig = WKWebViewConfiguration.init()
webView = WKWebView.init(frame: view.frame, configuration: wkconfig)
var url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "")!
webView.loadFileURL(url, allowingReadAccessTo: url)
view.addSubview(webView)
# 本地HTML加载资源
如果我们需要在 index.html 里面引入一个本地的 JS 文件或者 CSS 文件怎么做?
本地创建一个文件夹,比如叫 www。将创建好的 index.html 和 index.js 文件放入 www 文件夹中。html 就按照正常的语法引入 JS 文件就好。
//index.html <head> <script src="./index.js"> </script> </head> <body> <div>Hello world!</div> <div onclick="showAlert(this)">点击这里</div> </body> </html> //index.js function showAlert() { alert("Helloworld!"); }
将 www 文件夹拖入到 iOS 工程中,拖入的时候选择「Create folder references」
拖入后的项目目录结构如图
本地加载对应H5的方式需要更新,代码如下,最关键的是要给定对应文件夹的的读写权限
let wkconfig = WKWebViewConfiguration.init() webView = WKWebView.init(frame: view.frame, configuration: wkconfig) //这里要指定文件夹为 www,因为 www 是文件夹形式引入的 var url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "www")! let dirUrl = url.deletingLastPathComponent() webView.loadFileURL(url, allowingReadAccessTo: dirUrl) view.addSubview(webView)
读写权限主要是这个方法控制
func loadFileURL(_ URL: URL, allowingReadAccessTo readAccessURL: URL) -> WKNavigation?
If readAccessURL references a single file, only that file may be loaded by WebKit. If readAccessURL references a directory, files inside that file may be loaded by WebKit.
点击对应 H5 页上对应的元素节点,发现还是没有弹窗。原因是iOS 系统接管了 JS 的 alert 事件。当 alert 事件执行的时候,对应的视图控制器会执行
WKUIDelegate
协议的代理方法webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:
,代码如下//设置代理 webView.uiDelegate = self //current instance confirm WKUIDelegate protocol //实现代理方法 func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo) async { let alertCtrol = UIAlertController.init(title: message, message: nil, preferredStyle: .alert) alertCtrol.addAction(UIAlertAction.init(title: "OK", style: .cancel)) self.present(alertCtrol, animated: true) }
基本上按照如上步骤就搞定了 iOS 本地 html 页面加载 H5 的方式。
同理,如果想要在 html 中加载对应的本地一些资源文件,比如图片的话,直接放在和 html 相同的文件夹下,然后给文件夹读写权限就好了,和上面的步骤基本上一样。
# 使用Vue开发本地HTML
如果原生的 html 开发的有点烦的话,也可以在 html 里使用 Vue 框架开发。
iOS 本地加载 html 和外部浏览器加载 HTML 大部分行为都一致,我们可以使用 Vue 的 CDN 安装方式,直接在 html 内部使用 Vue 框架来让开发过程更顺畅。
html 代码如下,我们本地就正常加载 html 就可以。
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app" >
{{ message }}
<div> email:
<div @click="handleClick">fanthus@gmail.com</div>
</div>
</div>
<script>
const { createApp } = Vue
createApp({
data() {
return { message: 'Hello Vue!' }
},
methods: {
handleClick() { console.log('handleClick'); }
},
}).mount('#app')
</script>
# Vue 调用 iOS 方法
需要在 Vue 和 iOS 两端分别进行配置,基本思路还是 JSBridge
在 Vue 中使用
window.webkit.messageHandlers
对象发送消息给iOS端。比如你想要调用一个名为
sendMail
的原生方法,可以使用如下代码:handleClick() { window.webkit.messageHandlers.sendMail.postMessage({ "mail": "f@mail.com"}) }
在 iOS 端需要做如下事情
注册
sendMail
方法并实现//注册sendMail方法 let wkconfig = WKWebViewConfiguration.init() wkconfig.userContentController.add(self, name: "sendMail") //实现方法 @objc func sendMail(_ mail:String) { print("send mail \(mail)") }
当前视图控制器遵守
WKScriptMessageHandler
协议,并实现userContentController(_:didReceive:)
方法,Vue 点击回调会触发这个方法的执行。extension ViewController: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { if message.name == "sendMail" { guard let body = message.body as? [String: Any], let mail = body["mail"] as? String else { return } self.sendMail(mail) } } }
这样整个流程就配置完毕,点击 Vue 会触发对应 Native 端代码发邮件动作执行。
# iOS 调用 Vue 方法
iOS 和 Vue 两端分别配置如下
HTML Vue 端的配置如下。
创建需要被 Native 调用的方法
methods: { //1. 接收从 iOS 端出来参数 //2. 返回 H5 中定义的数据信息 email getData(info) { console.log('iOS call Vue code',info); return "f@gmail.com" } }
将方法放在全局可访问的函数中,挂载到 window 对象上。
created() { window.getData = this.getData },
iOS 端需主动调用对应 JS 代码
webView.evaluateJavaScript("getData('Hello from iOS')") { value, error in print("value \(value); error \(error)") }
最后的执行结果是对应 Vue 中方法输出
value Optional(f@gmail.com); error nil
# 开发调试方式
在 iOS 加载本地 HTML 的这种开发方式里,我使用 Visual Studio Code 作为我的 html 开发工具。VSCode 里有一个 Open with live Server 的插件非常好用,基本上在改动 html 的同时,界面也会同步刷新。
同一份前端代码在 VSCode 和 Xcode 里可以同时打开,VSCode 负责前端部分的开发,Xcode 负责 Native 部分的开发。可以在期间来回切换。就是有的时候脑子有点不够用。
有的时候需要查看前端的 console.log 日志,这部分日志没有办法在 Xcode 的终端输出。但是我们可以通过一些配置,看到前端的 console.log 日志。
# 查看前端console.log日志方式
查看应用前端文件输出的日志方式是
在手机端打开网页检查。路径是「设置」→ 「Safari浏览器」→ 「高级」→「网页检查器」,打开这个配置的开关。
初始化 wkwebview 实例的时候需要设置 webview 的属性
isInspectable
为 true,允许调试。如果不想上线后 release 版本被调试的话,这里可以设置一个DEBUG判断。在电脑端 Safari 浏览器里打开开发功能,同时手机端展示对应的网页,就能在目标手机上看到我们要调试的网页了。
点击我们要调试的 html 文件名字(这里是 index.html ),然后就会出现弹窗,点击能输出 console.log 的日志,观察到窗口如下输出。
这些都是一些基本的流程使用,如果大家有更好的实践方法的话,欢迎给我留言👏🏻👏🏻👏🏻。
参考地址:
- code linking folder for local HTML, CSS, javascript app (opens new window)
- Javascript console.log() in an iOS UIWebView (opens new window)
- 记一个ios WKWebView无法调试的坑 (opens new window)
关注我的微信公众号,我在上面会分享我的日常所思所想。