《成为大前端》系列 - 4.3 Native与JS通信-userContentController(iOS)

1,073 阅读2分钟

预置对象

前面我们讲过,WebView 启动时,客户端就向 JS 预置了一些方法,比如:

let data = ...
window.webkit.messageHandlers.iOSBridge.postMessage(data)

iOS 的 WKWebView 的 window 下,预置了messageHandlers,而我们通过 native 代码又可以预置iOSBridge对象, 这个对像包含了postMessage方法用于调用 native

那么如何实现呢

WKWebView添加userContentController

修改 ViewController 的代码,如下:

class ViewController: UIViewController, WKUIDelegate {

    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let config = WKWebViewConfiguration()

        // 添加一个Handler,名字为iOSBridge
        config.userContentController.add(BridgeHandler(), name: "iOSBridge")

        webView = WKWebView(frame: self.view.frame, configuration: config)
        webView.uiDelegate = self
        self.view.addSubview(webView)


        let url = URL(string:"https://www.baidu.com")!
        let request = URLRequest(url: url)
        webView.load(request)
    }
}

// 创建一个类,继承WKScriptMessageHandler。
// 注意:这里需要继承NSObject,暂不说明
class BridgeHandler : NSObject, WKScriptMessageHandler {

    // 当js调用这个bridge对象时,这个方法会被调用
    func userContentController(
        _ userContentController: WKUserContentController,
        didReceive message: WKScriptMessage)
    {
        print("WebView callNative ok.")
    }
}

js 端调用:

window.webkit.messageHandlers.iOSBridge.postMessage("Hello");

接下来我们要添加一个 HTML 页面,让上面这段 JS 运行起来

创建自己的网页

在写完以上代码后,我们需要测试代码运行情况。但是我们加载的是 baidu.com,没发运行我们的 js 代码, 因此我们需要自己的网页。

我们有两种方式:

  1. 起一个临时的服务器,比如:http://localhost:8080/test.html
  2. 直接将网页写到 iOS 项目中

为方便理解 iOS 加载项目中的网页,我们采用第 2 中方式

创建 html 文件

首先,新建 test.html 文件

创建完后添加如下代码:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <style type="text/css">
      html,
      body {
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        left: 0;
      }
      button {
        display: block;
        width: 100%;
        height: 30%;
        font-size: 50px;
      }
    </style>
  </head>
  <body>
    <script type="text/javascript">
      function onClickButton() {
        // 出现了,我们调用native的代码
        window.webkit.messageHandlers.iOSBridge.postMessage("Hello");
      }
    </script>

    <!-- 打算点击按钮调用native -->
    <button onclick="onClickButton()">Call Native</button>
  </body>
</html>

最后,ViewController 中的加载百度的代码改为:

// 获取项目中的test.html的url
let url = Bundle.main.url(forResource: "test", withExtension: "html")!
// 使用loadFileURL
webView.loadFileURL(url, allowingReadAccessTo: url)

注意:上面不再使用 load(request)了,而是本地路径

运行

运行前了解一下 XCode 的Console面板:

说明:

  1. 面板开关按钮
  2. 日志输出按钮

运行后,多点几次 Call Native 按钮就会得到第 2 点的输出,说明你已经完成了JS调用Native


兼容性:

MacOSX 10.15.X 版本系统运行调试程序,日志窗口不停的打 log

[Process] kill() returned unexpected error 1

解决方案:

  1. Xcode menu 打开: Product > Scheme > Edit Scheme
  2. 在 Environment Variables 添加设置 OS_ACTIVITY_MODE = disable
  3. 重新 Run 程序

附图: