使用wkwebview更改观察值

发布于 2025-02-01 10:04:36 字数 3273 浏览 4 评论 0原文

我正在为我的应用使用wkwebview。加载视图时,我想在本机上显示得分,而不是在wkwebview中显示。我使用组合框架工作来创建observableObject,以便在wkwebview中的分数更改时将我的分数显示给视图和其他视图。 。我使用window。我这样做是通过调用JS函数,该函数将消息发送到本机side webkit.messagehandlers.bridge.postmessage(“ 0”) ,并将已发送的分数分配给我的obseved> obsevedObject < /代码>。问题在本地。 USERCONTENTCONTROLLER函数,从wkwebview中处理消息,不断打印出分数并将分数重新分配给我的obsevedObject。它似乎被困在一个循环中。我提供了以下代码的简化版本。已经被困了几天了,似乎无法解决问题。

 //Holds the score
class Myscore:ObservableObject{
@Published var score = "0"

}


 //Wkwebview
struct WebView: UIViewRepresentable {
@ObservedObject var myScore : Myscore

 class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
    var webView: WKWebView?
    var myScore: Myscore
    init(myScore:Myscore) {
        self.myScore = myScore
        super.init()
        
    }
    
    
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        self.webView = webView
    }
    
    // receive message from wkwebview
    func userContentController(
        _ userContentController: WKUserContentController,
        didReceive message: WKScriptMessage
    ) {
        // This is where the issue occurs
        var newscore = message.body as! String
        myScore.score = newscore
        print(myScore.score)
    
    }
    
  }

func makeCoordinator() -> Coordinator {
    return Coordinator(myScore:myScore)
}

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(_ webView: WKWebView, context: Context) {
    guard let path: String = Bundle.main.path(forResource: "index", ofType: "html") 
 else { return }
    let localHTMLUrl = URL(fileURLWithPath: path, isDirectory: false)
    webView.loadFileURL(localHTMLUrl, allowingReadAccessTo: localHTMLUrl)
   }
 }

//Content View to display the Score
struct ContentView: View {
 @StateObject var myScore = Myscore()

var body: some View {
    VStack {
     
        Text("Your Score is\( myScore.score)")
        WebView(myScore: myScore)
    }
   }
  }

在这里编辑是HTML侧:

<!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>
    window.onload = function () {
        webkit.messageHandlers.bridge.postMessage("98 points")

    
    }
 
</script>
</body>
</html>

编辑2:这是控制台打印的内容

I am using WkWebView for my app. When the view is loaded I would like to display the score Natively and not in the WkWebView. I used the combine Frame work to create an ObservableObject in order to display my score to the view and other views when the score changes in the WkWebview. I use window.onload to get the most recent score and display it when the page first renders. I do so by calling a JS function which sends a message to the Native side webkit.messageHandlers.bridge.postMessage("0") with the score and assign the sent score to my ObservedObject. The issue is on the Native side. The UserContentController function, which handles the message from the WkWebview, keeps printing out the score and reassigning the score to my ObservedObject. It seems to be stuck in a loop. I provided a simplified version of the code below. Have been stuck on this for a few days now and cant seem to fix the issue.

 //Holds the score
class Myscore:ObservableObject{
@Published var score = "0"

}


 //Wkwebview
struct WebView: UIViewRepresentable {
@ObservedObject var myScore : Myscore

 class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
    var webView: WKWebView?
    var myScore: Myscore
    init(myScore:Myscore) {
        self.myScore = myScore
        super.init()
        
    }
    
    
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        self.webView = webView
    }
    
    // receive message from wkwebview
    func userContentController(
        _ userContentController: WKUserContentController,
        didReceive message: WKScriptMessage
    ) {
        // This is where the issue occurs
        var newscore = message.body as! String
        myScore.score = newscore
        print(myScore.score)
    
    }
    
  }

func makeCoordinator() -> Coordinator {
    return Coordinator(myScore:myScore)
}

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(_ webView: WKWebView, context: Context) {
    guard let path: String = Bundle.main.path(forResource: "index", ofType: "html") 
 else { return }
    let localHTMLUrl = URL(fileURLWithPath: path, isDirectory: false)
    webView.loadFileURL(localHTMLUrl, allowingReadAccessTo: localHTMLUrl)
   }
 }

//Content View to display the Score
struct ContentView: View {
 @StateObject var myScore = Myscore()

var body: some View {
    VStack {
     
        Text("Your Score is\( myScore.score)")
        WebView(myScore: myScore)
    }
   }
  }

Edit here is the html side:

<!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>
    window.onload = function () {
        webkit.messageHandlers.bridge.postMessage("98 points")

    
    }
 
</script>
</body>
</html>

Edit 2: This is what the console prints out
This is what the console prints out

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

清风无影 2025-02-08 10:04:36

等等来查看周期加载。

在这里,我可以

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

    // make here initial loading !!!
    guard let path: String = Bundle.main.path(forResource: "index", ofType: "html") 
 else { return _wkwebview }

    let localHTMLUrl = URL(fileURLWithPath: path, isDirectory: false)
    _wkwebview.loadFileURL(localHTMLUrl, allowingReadAccessTo: localHTMLUrl)

    
    return _wkwebview
}

  func updateUIView(_ webView: WKWebView, context: Context) {
     // reload should be made here only if base url changed
     // externally
   }

通过更新myScore进行updateUiview调用 13.4 / iOS 15.5

Here I see cycle loading by updating Myscore which result in updateUIView call and so forth...

Initial loading should be placed into makeUIView

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

    // make here initial loading !!!
    guard let path: String = Bundle.main.path(forResource: "index", ofType: "html") 
 else { return _wkwebview }

    let localHTMLUrl = URL(fileURLWithPath: path, isDirectory: false)
    _wkwebview.loadFileURL(localHTMLUrl, allowingReadAccessTo: localHTMLUrl)

    
    return _wkwebview
}

  func updateUIView(_ webView: WKWebView, context: Context) {
     // reload should be made here only if base url changed
     // externally
   }

Tested with Xcode 13.4 / iOS 15.5

谁把谁当真 2025-02-08 10:04:36

得分应为@bindingupdateUiview将在更改时自动调用。不幸的是,这是Swiftui结构的众多无证件魔术行为之一。

FYI observableObject旨在将组合管道分配给@published。尝试坚持数据的结构,并使用@State@binding以使您的值类型具有参考类型语义。

score should be @Binding and updateUIView will be called automatically when it changes. Unfortunately this is one of the many undocumented magic behaviours of SwiftUI's structs.

FYI ObservableObject is designed to assign a Combine pipeline to an @Published. Try and stick to structs for your data and use @State and @Binding to make your value types have reference type semantics.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文