WKUserContentController与WKScriptMessageHandler配置错误,HTML无法向Swift发消息
问题描述
- 使用
WKUserContentController和WKScriptMessageHandler尝试从嵌入的HTML向Swift发送消息,修改布尔值isPlaying - HTML中的
onReady代码块已执行,但消息始终无法发送,判断条件window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.toggleMessageHandler返回false,说明WebView未正确配置 - 连接Mac查看开发者菜单,设备下看不到已打开的WebView,进一步确认WebView配置存在问题
- 需求是让Swift端知晓视频已准备好播放,为之前问题的延伸
问题代码
struct SmartReelView: UIViewRepresentable { let link: String @Binding var isPlaying: Bool func makeCoordinator() -> Coordinator { Coordinator(self) } func makeUIView(context: Context) -> WKWebView { let webConfiguration = WKWebViewConfiguration() webConfiguration.allowsInlineMediaPlayback = true let webView = WKWebView(frame: .zero, configuration: webConfiguration) webView.navigationDelegate = context.coordinator let userContentController = WKUserContentController() userContentController.add(context.coordinator, name: "toggleMessageHandler") webView.configuration.userContentController = userContentController loadInitialContent(in: webView) return webView } func updateUIView(_ uiView: WKWebView, context: Context) { let jsString = "isPlaying = \((isPlaying) ? \"true\" : \"false\"); watchPlayingState();" uiView.evaluateJavaScript(jsString, completionHandler: nil) } class Coordinator: NSObject, WKScriptMessageHandler, WKNavigationDelegate { var parent: SmartReelView init(_ parent: SmartReelView) { self.parent = parent } func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { print("message recieved") self.parent.isPlaying = true } } private func loadInitialContent(in webView: WKWebView) { let embedHTML = """ <style> .iframe-container iframe { top: 0; left: 0; width: 100%; height: 100%; } </style> <div class="iframe-container"> <div id="player"></div> </div> <script> var tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); var player; function onYouTubeIframeAPIReady() { player = new YT.Player('player', { width: '100%', videoId: '\(link)', playerVars: { 'playsinline': 1, 'controls': 0}, events: { 'onReady': function(event) { if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.toggleMessageHandler) { window.webkit.messageHandlers.toggleMessageHandler.postMessage({"message": "click now" }); } } } }); } </script> """ webView.scrollView.isScrollEnabled = false webView.loadHTMLString(embedHTML, baseURL: nil) } }
解决方案
核心问题是WKWebView初始化后,其configuration属性是只读的,无法直接修改。原代码中先创建WebView,再给webView.configuration.userContentController赋值的操作无效,导致消息处理器未被正确添加。
修正步骤:
- 先创建并配置
WKUserContentController,添加消息处理器 - 将配置好的
userContentController赋值给WKWebViewConfiguration - 用这个配置好的
webConfiguration初始化WKWebView
修正后的makeUIView函数代码:
func makeUIView(context: Context) -> WKWebView { // 先创建并配置userContentController let userContentController = WKUserContentController() userContentController.add(context.coordinator, name: "toggleMessageHandler") // 配置webConfiguration,加入userContentController let webConfiguration = WKWebViewConfiguration() webConfiguration.allowsInlineMediaPlayback = true webConfiguration.userContentController = userContentController // 用配置好的webConfiguration初始化WebView let webView = WKWebView(frame: .zero, configuration: webConfiguration) webView.navigationDelegate = context.coordinator loadInitialContent(in: webView) return webView }
额外优化建议:
- 可以在HTML的
onReady里增加调试日志,比如console.log(window.webkit),方便排查是否能访问到webkit对象 - 确保WebView在SwiftUI中被正确渲染(比如没有被设置为frame .zero或者被其他视图遮挡)
内容的提问来源于stack exchange,提问作者Ahmed Zaidan
相关产品推荐
相关产品推荐

