我试图通过禁用YouTube视频并在其上方放置一个可点击的清晰视图来覆盖YouTube默认的播放和暂停按钮.当点击清晰视图时,它会更改决定是否播放视频的绑定(此视频也会自动播放).

目前,isVideoPlaying变量确实正确地更改了,并且HTML确实接受了更改.现在的问题是,我添加了一个事件侦听器"onClick",这永远不会执行.我需要将此事件侦听器更改为更改isVideoPlaying.

但我不知道该怎么做.点击Clear View会暂停或播放视频的唯一原因是,当点击Clear View时,会迅速重新加载整个视频,从而使其通过onReady部分.这是另一个问题,我不希望YouTube视频在每次点击Clear View时都重新加载.我想让它简单地播放和暂停,而不需要重新加载.

作为参考,我正试着用YouTube短裤制作TikTok的克隆.

import SwiftUI
import WebKit

struct SingleVideoView: View {
    let link: String
    @State private var isVideoPlaying = true

    var body: some View {
        ZStack {
            SmartReelView(link: link, isPlaying: $isVideoPlaying)

            Button("", action: {}).disabled(true)//to disable YouTube video
            
            Color.gray.opacity(0.001)
                .onTapGesture {
                    isVideoPlaying.toggle()
                }
        }
        .ignoresSafeArea()
    }
}

struct SwiftUIView_Previews: PreviewProvider {
    static var previews: some View {
        SingleVideoView(link: "-q6-DxWZnlQ")
    }
}

struct SmartReelView: UIViewRepresentable {
    let link: String
    @Binding var isPlaying: Bool
    @Environment(\.colorScheme) var colorScheme

    func makeUIView(context: Context) -> WKWebView {
        let webConfiguration = WKWebViewConfiguration()
        webConfiguration.allowsInlineMediaPlayback = true
        return WKWebView(frame: .zero, configuration: webConfiguration)
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        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: { 'autoplay': 1, 'playsinline': 1 },
                    events: {
                        'onReady': function(event) {
                            var isPlaying = \((isPlaying) ? "true" : "false");
                            if (isPlaying) {
                                event.target.mute();
                                if (isPlaying) {
                                    event.target.playVideo();
                                }
                            }
                        }
                    }
                });
            }
        
            function watchPlayingState() {
                if (isPlaying) {
                    player.playVideo();
                } else {
                    player.pauseVideo();
                }
            }
            
            document.addEventListener('click', function() {
                watchPlayingState();
            });

        </script>
        """

        uiView.scrollView.isScrollEnabled = false
        uiView.loadHTMLString(embedHTML, baseURL: nil)
    }
}

更新

在提供以下答案后,我进一步修改了代码:

这段新代码收到错误"类型‘SmartReelView’不符合协议‘UIView’".

struct SmartReelView: UIViewRepresentable {
    let link: String
    @Binding var isPlaying: Bool
    @Environment(\.colorScheme) var colorScheme
    var webView: WKWebView?

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, WKNavigationDelegate {
        var parent: SmartReelView
        
        init(_ parent: SmartReelView) {
            self.parent = parent
        }
        
        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            parent.webView = webView
        }
    }

    func makeUIView(context: Context) -> WKWebView {
        let webConfiguration = WKWebViewConfiguration()
        webConfiguration.allowsInlineMediaPlayback = true
        let webView = WKWebView(frame: .zero, configuration: webConfiguration)
        
        webView.navigationDelegate = context.coordinator
        return webView
    }

    mutating func updateUIView(_ uiView: WKWebView, context: Context) {
        if webView == nil {
            webView = uiView
            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;
                var isPlaying = true;
                function onYouTubeIframeAPIReady() {
                    player = new YT.Player('player', {
                        width: '100%',
                        videoId: '\(link)',
                        playerVars: { 'autoplay': 1, 'playsinline': 1 },
                        events: {
                            'onReady': function(event) {
                                event.target.mute();
                                event.target.playVideo();
                            }
                        }
                    });
                }
            
                function watchPlayingState() {
                    if (isPlaying) {
                        player.playVideo();
                    } else {
                        player.pauseVideo();
                    }
                }
            </script>
            """

            uiView.scrollView.isScrollEnabled = false
            uiView.loadHTMLString(embedHTML, baseURL: nil)
        }

        let jsString = "isPlaying = \((isPlaying) ? "true" : "false"); watchPlayingState();"
        webView?.evaluateJavaScript(jsString, completionHandler: nil)
    }
}

推荐答案

您的SWIFT代码将更新isVideoPlaying,但您的Java代码未检测到此更改.SWIFT和JavaScript之间的一种标准通信方式是使用WKWebView.evaluateJavaScript.当isVideoPlaying变量发生更改时,您可以使用它来运行JavaScript代码.

每次点击清晰画面时,你的视频都会重新加载,这不是你想要的行为:你应该避免重新加载整个WKWebView个视频.你可以使用YouTube的Java API控制视频的播放和暂停状态.

就这样.

  1. 更新您的SmartReelView以存储对WKWebView的引用.
  2. 添加Coordinator级以设置WKNavigationDelegate,如in this thread.
  3. 使用evaluateJavaScript可在SWIFT isVideoPlaying变量更改时更新isPlaying JavaScript变量.
struct SmartReelView: UIViewRepresentable {
    // (existing code)
    
    // Point 1: Store a reference to WKWebView
    private var webView: WKWebView?

    // Point 2: Add a Coordinator class to set up WKNavigationDelegate
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, WKNavigationDelegate {
        var parent: SmartReelView
        
        init(_ parent: SmartReelView) {
            self.parent = parent
        }
        
        // WKNavigationDelegate method to capture the loaded WKWebView
        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            parent.webView = webView
        }
    }

    func makeUIView(context: Context) -> WKWebView {
        let webConfiguration = WKWebViewConfiguration()
        webConfiguration.allowsInlineMediaPlayback = true
        let webView = WKWebView(frame: .zero, configuration: webConfiguration)
        
        // Set the navigation delegate to your Coordinator
        webView.navigationDelegate = context.coordinator
        return webView
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        // Only load the HTML when the webView is nil, prevents reloading
        if webView == nil {
            webView = uiView
            // Load HTML here (as in your existing code)
        }

        // Point 3: Use evaluateJavaScript to update isPlaying in JavaScript
        let jsString = "isPlaying = \((isPlaying) ? "true" : "false"); watchPlayingState();"
        webView?.evaluateJavaScript(jsString, completionHandler: nil)
    }
}

在您的JavaScript中:

  • onReady函数中删除var isPlaying = ...行.
  • 确保watchPlayingState()使用全局isPlaying变量.

这样,您的SWIFT代码将直接设置JavaScript isPlaying变量,并调用watchPlayingState()来相应地播放或暂停视频.它不会重新加载视频,而只是使用YouTube API进行控制.


如果您计划在像Coordinator这样的嵌套类中修改webView,则将webView设置为非私有是正确的.

由于UIViewRepresentable需要非Mutations 函数,因此最好避免使用关键字mutating.使用类而不是SmartReelView的 struct 来避免这种情况.类是引用类型,它们的方法不需要mutating关键字.

错误"Type 'SmartReelView' does not conform to protocol 'UIViewRepresentable'"很可能是因为您的代码中有两个版本的SmartReelView.其中一个(new)具有makeCoordinator()功能,而另一个(old)不具有.这可能会给SWIFT编译器带来混乱.如果不需要旧版本,请将其删除.

您可以try :

import SwiftUI
import WebKit

// (Same as above)

// Using a class instead of struct
class SmartReelView: NSObject, UIViewRepresentable {
    let link: String
    @Binding var isPlaying: Bool
    @Environment(\.colorScheme) var colorScheme
    var webView: WKWebView?
    
    init(link: String, isPlaying: Binding<Bool>) {
        self.link = link
        self._isPlaying = isPlaying
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, WKNavigationDelegate {
        var parent: SmartReelView
        
        init(_ parent: SmartReelView) {
            self.parent = parent
        }
        
        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            parent.webView = webView
        }
    }

    func makeUIView(context: Context) -> WKWebView {
        let webConfiguration = WKWebViewConfiguration()
        webConfiguration.allowsInlineMediaPlayback = true
        let webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.navigationDelegate = context.coordinator
        return webView
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        if webView == nil {
            webView = uiView
            // (Same HTML and JavaScript injection)
        }
        let jsString = "isPlaying = \((isPlaying) ? "true" : "false"); watchPlayingState();"
        webView?.evaluateJavaScript(jsString, completionHandler: nil)
    }
}

// (Rest of your code)

我已经将SmartReelView转换为一个类,并且只在这个类中保留了makeCoordinator函数.我还更新了初始化器,将Binding作为isPlaying,这使得它与您在SingleVideoView中使用它的方式兼容.

Html相关问答推荐

z—index总是 destruct 我的头,我该如何修复它?

如何增加行背景的宽度而不影响上面的内容?""' HTML CSS

如何使我的组织 struct 图的连接线响应?

Angular Project中的星级 Select

标签数据的XPath?

使用css第n个子元素,如何迭代Y组中的X个元素?

创建一个带有div和径向渐变的全宽半圆

背景可点击,而不是文本

如何将内容元素放在侧边栏旁边?

Bootstrap 5.3.2模式页脚左填充还是左边距?

使具有FLEX父项的溢出自动的子元素收缩

在TWebLabel标题中设置换行符/换行符的方法?

如何在css中在span中绘制圆弧?

如何在没有包装元素的情况下在React(Next.js)中呈现HTML注释

如何在 Bootstrap 5 中将两个导航栏元素放在末尾?

HTML 重定向到当前服务器

在DIV上应用梯度模糊未如预期工作

使用 R 中的 rvest 包获取搜索结果的第一个链接

根据百分比用多种 colored颜色 填充SVG路径

动态使用时波浪号不转换为绝对路径