首页 > 解决方案 > 使用 WKWebView 通过 SwiftUI 从任何页面同步数据 - 但有更好的方法吗?

问题描述

在下面的代码中,我将 HTML 页面加载到 WKWebView 中,只是为了获取它们的第一个 H1 标记的内容。除了任何类型的页面在其第一个 H1 标记中呈现的内容之外,我不需要其他任何内容......

除了为此初始化 WKWebView 之外,是否还有更有效/更高效的方法?

.frame(width: 0, height: 0)修饰符也让我有点恼火)

import SwiftUI
import WebKit
import WebView /* add this Swift Package Dependency https://github.com/kylehickinson/SwiftUI-WebView */

struct ContentView: View {
    @StateObject private var viewModel: ContentViewModel
    init() {
        let viewModel = ContentViewModel()
        _viewModel = StateObject(wrappedValue: viewModel)
    }

    var body: some View {
        VStack(spacing: 30) {
            Button("load plain H1 tag") {
                viewModel.webViewStore.webView.loadHTMLString("<h1>H1</h1>", baseURL: nil)
            }
            Button("load body where h1 is appended by JavaScript") {
                viewModel.webViewStore.webView.loadHTMLString("<body></body><script>(function(){ let h1 = document.createElement('h1'); h1.textContent = 'H1 JavaScript'; document.body.appendChild(h1); })()</script>", baseURL: nil)
            }
            Button("load apple.com/iphone") {
                viewModel.webViewStore.webView.load(URLRequest(url: URL(string: "https://apple.com/iphone")!))
            }
            Button("load hackingwithswift.com") {
                viewModel.webViewStore.webView.load(URLRequest(url: URL(string: "https://hackingwithswift.com")!))
            }
            Group {
                Text("the first H1 element on ")
                + Text(viewModel.webViewStore.webView.url?.description ?? "...").foregroundColor(.accentColor)
                + Text(" is")
            }
            .font(.subheadline)

            Text(viewModel.h1)
                .font(.largeTitle)
                .foregroundColor(.green)

            WebView(webView: viewModel.webViewStore.webView)
                .frame(width: 0, height: 0)
        }
    }
}

class ContentViewModel: NSObject, WKScriptMessageHandler, ObservableObject {
    @Published var h1 = "..."
    @Published var webViewStore: WebViewStore

    override init() {
        let userContentController = WKUserContentController()
        let configuration = WKWebViewConfiguration()
        let userScript = WKUserScript(
            source: """
              function elementReady(selector) {
                return new Promise((resolve, reject) => {
                  let el = document.querySelector(selector)
                  if (el) {
                    resolve(el)
                  }
                  new MutationObserver((mutationRecords, observer) => {
                    Array.from(document.querySelectorAll(selector)).forEach((element) => {
                      resolve(element)
                      observer.disconnect()
                    })
                  }).observe(document.documentElement, {
                    childList: true,
                    subtree: true,
                  })
                })
              }
              elementReady("h1").then((titleElement) => {
                window.webkit.messageHandlers.h1.postMessage(titleElement.textContent)
              })
            """,
            injectionTime: .atDocumentStart,
            forMainFrameOnly: false,
            in: .defaultClient
        )
        userContentController.addUserScript(userScript)
        configuration.userContentController = userContentController
        let webView = WKWebView(frame: .zero, configuration: configuration)
        webViewStore = WebViewStore(webView: webView)

        super.init()
        userContentController.add(self, contentWorld: .defaultClient, name: "h1")
    }
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "h1" {
            h1 = message.body as! String
        }
   }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

标签: javascriptswiftswiftuiwebkitwkwebview

解决方案


推荐阅读