要从嵌入的 HTML 与 Swift 进行通信以更改布尔值,您需要使用WKUserContentController https://developer.apple.com/documentation/webkit/wkusercontentcontroller及其委托方法来监听自定义 JavaScript 事件。您可以使用window.webkit.messageHandlers[handlerName].postMessage(message) from WKScriptMessageHandler https://developer.apple.com/documentation/webkit/wkscriptmessagehandler在 JavaScript 中将消息从 JavaScript 发送到 Swift (看到这个问题 https://stackoverflow.com/q/62035494/6309用于说明)。
The SmartReelView
需要创建自己的Coordinator https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable/coordinator并符合WKScriptMessageHandler
处理 JavaScript 消息:
struct SmartReelView: UIViewRepresentable {
let link: String
@Binding var isPlaying: Bool
@Binding var click: 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
// Create user content controller
let userContentController = WKUserContentController()
// Add the message handler script
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) {
if message.name == "toggleMessageHandler", let messageBody = message.body as? [String: Any] {
if let messageValue = messageBody["message"] as? String, messageValue == "click now" {
DispatchQueue.main.async {
self.parent.click = true
}
}
}
}
}
// existing loadInitialContent function
}
A Coordinator
引入的类符合WKScriptMessageHandler
。这将处理来自 JavaScript 的消息。
Coordinator 作为脚本消息处理程序添加到 WebView 中,名称为“toggleMessageHandler”。
在你的 HTML 中onReady
JavaScript 函数,一条消息被发布到该脚本消息处理程序。斯威夫特的userContentController(_:didReceive:)
然后接收该消息,处理它,并更新@Binding var click
.
这应该允许 Swift 代码对 HTML 中的事件做出反应,改变click
变量时onReady
JavaScript 中触发事件。
似乎从未收到过该消息。在“的开头添加打印userContentController
“永远不要跑。
这可能来自操作的顺序,也可能来自于所提供的代码中不明显的特定环境约束。鉴于打印内部userContentController
方法没有显示,这表明该方法从未被调用。这可能是由于 JavaScript 未成功发送消息,或者是因为 Swift 中的消息处理程序未正确设置。
确保WKUserContentController
及其消息处理程序已设置before任何网页内容均已加载。您在您的系统中正确地执行了此操作makeUIView
功能,但值得强调。
并仔细检查您的 JavaScript 是否确实执行了将消息发布到的部分window.webkit.messageHandlers.toggleMessageHandler
。您可以将控制台日志添加到 JavaScript 中,看看它是否达到了这一点。
您已经设置了navigationDelegate
给您的协调员,但请确保您还设置了WKScriptMessageHandler
通过使用userContentController.add(context.coordinator, name: "toggleMessageHandler")
.
The onReady
块确实会执行,但是“.postMessage({"message": "click now" });
“ 从未达到,这意味着if
其之前的语句被评估为 false。删除if
语句仍然不会触发该消息。这意味着window.webkit
is nil.
我还尝试在真实设备上运行它,但这没有帮助。唯一的另一件事是每次播放和暂停视频时我都会收到一条警告:“originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions
"
确保WKUserContentController
并且消息处理程序已在加载网页内容之前设置。如果没有这个可能会导致无效window.webkit
目的。并检查WebView是否完全初始化onReady
函数被调用。这WKScriptMessageHandler
那时可能尚未完全设置,因此window.webkit
可能不可用。
您可以尝试调用window.webkit.messageHandlers.toggleMessageHandler.postMessage({"message": "click now"});
直接从 Safari 的 Web Inspector 控制台手动检查网页完全加载后 JavaScript 和 Swift 之间的桥梁是否正常工作。这至少可以排除本机代码的任何问题,并将范围缩小到 JavaScript 方面。
有关权利的警告消息,例如com.apple.runningboard.assertions.webkit
and com.apple.multitasking.systemappassertions
通常表明应用程序正在尝试执行需要特殊权限或权利的操作。尽管这些警告不一定会影响WKScriptMessageHandler
,确保您拥有所有必要的权限Info.plist https://developer.apple.com/documentation/bundleresources/information_property_list。也可以看看哪里Info.plist在 Xcode 13 中 https://sarunw.com/posts/where-is-info-plist/.
I think WKUserContentController
只是设置不正确。当我插入 iPhone 并运行该应用程序时,转到 Mac 上的 YouTube 视频并按“开发”,然后将鼠标悬停在 iPhone 上,但我没有看到任何加载的网页。退一步来说,这是我想要更改 bool 值的唯一原因,因为如果我这样做,我可以监听 var 的更改,然后在onChange
块我可以设置isPlaying
变量设置为 true 将返回并取消暂停视频。这只是为了播放视频(UNMUTED
)准备好后。由于某种原因打电话watchPlayingState
from onReady
不起作用。
我认为 YouTube AGO 会通过 js 并确保暂停操作是由用户的物理操作激发的?否则它怎么知道不播放视频。呼唤watchPlayingState
in the onReady
不起作用(但在自动播放视频之前将视频静音)。但如果我放一个简单的onAppear
在我的主视图中并切换isPlaying
然后视频会自动播放声音(也许 API 被欺骗认为是用户造成的)。一切onAppear
确实是让watchPlayingState
run.
您遇到的行为很可能是由于 YouTube 的 API 和浏览器安全准则对自动播放有声视频施加的限制造成的。由于浏览器政策限制自动播放,视频通常不会自动播放有声视频,除非视频被静音或由用户操作触发。即增强用户体验并减少意外的有声视频播放。 YouTube API 可能有类似的限制。即使您尝试在中使用 JavaScript 播放视频onReady
事件时,API 可能会检查此请求是否是由用户操作引起的。
鉴于您在检查设备时没有看到任何加载的网页,这确实会引发以下问题:WKUserContentController
并且 JavaScript 桥设置正确。如果没有在正确的时间添加消息处理程序,JavaScript 将无法调用该消息处理程序。
Your onAppear
这个技巧可能会起作用,因为 SwiftUI 在视图出现后发送此消息,从而可能欺骗 YouTube API 将其视为用户操作。
如果目标是自动播放有声视频,考虑到网络技术和 YouTube API 的限制,如果不进行一些用户操作,您可能无法实现此目标。然而,使用 Swift 的方法@Binding
变量和onChange
检测视频何时准备好播放是合理的。您本质上是在尝试在 Swift 代码和 JavaScript 代码之间创建握手来协调自动播放。
确保WKUserContentController
and WKScriptMessageHandler
已正确设置;否则,window.webkit
对象在 JavaScript 上下文中不可用,并且你的 Swift@Binding
将不会收到更新。