如何从 SwiftUI 监听 JavaScript 回调?

问题描述 投票:0回答:3

我是 SwiftUI 的新手,我在 SwiftUI 中创建了这样的 WebView

struct WebView: UIViewRepresentable {
    @Binding var title: String
    var url: URL
    var loadStatusChanged: ((Bool, Error?) -> Void)? = nil

    func makeCoordinator() -> WebView.Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> WKWebView {
        let view = WKWebView()
        view.navigationDelegate = context.coordinator
        view.load(URLRequest(url: url))
        return view
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        // you can access environment via context.environment here
        // Note that this method will be called A LOT
    }

    func onLoadStatusChanged(perform: ((Bool, Error?) -> Void)?) -> some View {
        var copy = self
        copy.loadStatusChanged = perform
        return copy
    }

    class Coordinator: NSObject, WKNavigationDelegate,WKScriptMessageHandler {
        @State private var studentAppID = UserDefaults.standard.string(forKey: "studentAppID")
        let parent: WebView

        init(_ parent: WebView) {
            self.parent = parent
        }

        func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
            print(webView.url as Any)
            parent.loadStatusChanged?(true, nil)
        }

        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            print(webView as Any)
            print(webView.url as Any)
            parent.title = webView.title ?? ""
            webView.evaluateJavaScript("document.getElementsByName('applnID')[0].value='\(studentAppID!)'", completionHandler: nil)
            parent.loadStatusChanged?(false, nil)
        }

        func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
            parent.loadStatusChanged?(false, error)
        }
        
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
//            guard let dict = message.body as? [String : AnyObject] else {
//                return
//            }
            
            print(message.body)
        }

    }
}

我想配置功能,以便当用户单击 WebView 上的按钮标签时我可以收到 WebView 的响应
我在 swift 中找到了此代码,但不明白如何在 SwiftUI 中使用它 代码是这样的

class ViewController: UIViewController, WKScriptMessageHandler {
  let content = """
  <!DOCTYPE html><html><body>
  <button onclick="onClick()">Click me</button>
  <script>
  function onClick() {
    window.webkit.messageHandlers.backHomePage.postMessage("success");
  }
  </script>
  </body></html>
  """

  override func viewDidLoad() {
    super.viewDidLoad()

    let config = WKWebViewConfiguration()
    config.userContentController = WKUserContentController()
    config.userContentController.add(self, name: "backHomePage")

    let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 200, height: 200), configuration: config)

    view.addSubview(webView)

    webView.loadHTMLString(content, baseURL: nil)
  }

  func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    print(message.body)
  }
}

如本链接中的流程

ios中如何从javascript接收回调?

我可以在 SwiftUI 中配置相同的配置吗?如果是的话,那么如何配置?

javascript swiftui wkwebview
3个回答
4
投票

您实施的内容是正确的,但是您错过了

WKWebViewConfiguration
对象。

您需要创建 Web 视图配置来注册消息处理程序以供您的 javascript 代码调用,请参见下文:

  1. 创建一个可以从 JavaScript 代码接收回调的类:

    let scriptMessageHandler = ScriptMessageHandler()
    
    class ScriptMessageHandler: NSObject, WKScriptMessageHandler {
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            print(message.body)
        }
    }
    
  2. 创建一个配置对象并传递

    ScriptMessageHandler
    的对象作为处理程序。

    let config = WKWebViewConfiguration()
    config.userContentController = WKUserContentController()
    config.userContentController.add(scriptMessageHandler, name: "backHomePage")```
    

您可以在创建 Web 视图对象并将此配置传递到您的 Web 视图之前,在

makeUIView(context:) -> WKWebView
方法实现中执行此操作。

在Javascript中,可以像下面这样调用上面的配置:

function onClick() {
   window.webkit.messageHandlers.backHomePage.postMessage("success");
}

当从 JavaScript 调用上述函数时,您将在

userContentController(_ didReceive:)
实现中收到回调。


0
投票

基于@KishanSadhwani的回答,下面是一个通过测试的示例:


import SwiftUI
import WebKit

let testHTML = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello Alert</title>
</head>
<body>

<button onclick="showAlert()">Click me to send message to native app</button>

<script>
    function showAlert() {
        window.webkit.messageHandlers.NAME.postMessage("[MESSAGE TO APP]");
    }
</script>

</body>
</html>

"""

class LogBookScriptMessageHandler: NSObject, WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        print(message.body)
    }
}

struct LogBookWebView: UIViewRepresentable {
    typealias UIViewType = WKWebView

    func makeUIView(context: Context) -> WKWebView {
        let config = WKWebViewConfiguration()

        let handler = LogBookScriptMessageHandler()
        config.userContentController.add(handler, name: "NAME")

        let webView = WKWebView(frame: .zero, configuration: config)

        webView.loadHTMLString(testHTML, baseURL: nil)

        return webView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {

    }
}

#Preview {
    LogBookWebView()
}

希望这有帮助。


0
投票

我自己检查了这个,它对我来说工作正常。

struct WebView: UIViewRepresentable {
            func makeUIView(context: Context) -> WKWebView {
                let coordinator = makeCoordinator()
                let userContentController = WKUserContentController()
                userContentController.add(coordinator, name: "bridge")
                let configuration = WKWebViewConfiguration()
                configuration.userContentController = userContentController
                let wkwebview = WKWebView(frame: .zero, configuration: configuration)
                wkwebview.navigationDelegate = coordinator
                return wkwebview
            }
    
        func updateUIView(_ uiView: WKWebView, context: Context) {
                let content = """
                  <!DOCTYPE html>
                  <html>
                  <head>
                  <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, minimum-scale=1, viewport-fit=cover">
                  </head>
                  <body>
                  <button>click me</button>
                  <hr/>
                  <div id="log"></div>
                  <script>
                    const log = (msg) => {
                      const p = document.createElement('p')
                      p.textContent = msg
                      document.querySelector('#log').append(p)
                    }
                    // to receive messages from native
                    webkit.messageHandlers.bridge.onMessage = (msg) => {
                      log('from native:' + msg)
                    }
        
                    document.querySelector('button').addEventListener('click', () => {
                      log(typeof webkit.messageHandlers.bridge.postMessage)
                      // send messages to native
                      webkit.messageHandlers.bridge.postMessage('{"msg": "hello?","id": ' + Date.now() + '}')
                    })
                  </script>
                  </body>
                  </html>
                  """
                uiView.loadHTMLString(content, baseURL: nil)
            }
    
        func makeCoordinator() -> Coordinator {
            return Coordinator()
        }
    
        class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
    
            private var webView: WKWebView?
    
            func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
                self.webView = webView
                print("WebView: navigation finished")
            }
    
            func webViewDidFinishLoad(webView: WKWebView) {
                print("Loaded: \(String(describing: webView.url))")
            }
    
            func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
                print("Loaded: \(String(describing: webView.url))")
        
    
            }
        
        // this method is used to get the message from the Webview
                func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
                    print("Signal: \(message.body)")
                    messageToWebview(msg: "iOS Native")
                }
        
        // this method is used to send the message to the Webview
                func messageToWebview(msg: String) {
                    self.webView?.evaluateJavaScript("webkit.messageHandlers.bridge.onMessage('\(msg)')")
                }
            }
    }

希望这对您有帮助

© www.soinside.com 2019 - 2024. All rights reserved.