我有一个基本的选项卡视图,可以包含不同顺序的图像和视频array.当前,当视频视图变为当前选项卡时,视频播放器不播放.有时候可以显示图像,但视频播放器的音频仍在播放.如何制作一个简单的选项卡视图,可以以任何顺序显示图像和视频,并在视频成为当前选项卡时播放视频,并在选项卡切换时暂停它们?

import SwiftUI
import Kingfisher
import AVFoundation

struct MainView: View {
    @State private var selection = 0
    @State var content: [uploadContent] = [uploadContent(isImage: false, videoURL: URL(string: "https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/stories%2F73F8CF05-39AB-4A8F-AAEF-C70C9A57B22D.mp4?alt=media&token=5b5a63e3-e5af-4e2f-84c4-416abd554b84")!),
                                           uploadContent(isImage: true, imageURL: "https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/messages%2F19EAAFB0-0AC1-4100-88DC-F9980531D559?alt=media&token=5b206605-94e2-43f3-9dfb-1922cfecc989"),
                                           uploadContent(isImage: false, videoURL: URL(string: "https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/stories%2F73F8CF05-39AB-4A8F-AAEF-C70C9A57B22D.mp4?alt=media&token=5b5a63e3-e5af-4e2f-84c4-416abd554b84")!),
                                           uploadContent(isImage: false, videoURL: URL(string: "https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/stories%2F73F8CF05-39AB-4A8F-AAEF-C70C9A57B22D.mp4?alt=media&token=5b5a63e3-e5af-4e2f-84c4-416abd554b84")!),
                                           uploadContent(isImage: true, imageURL: "https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/messages%2F01659DB0-C97D-4FB3-A0B4-0F0302FA463B?alt=media&token=9e5b0553-c081-443e-a263-7e0e81630281")]

    var body: some View {
        TabView(selection: $selection) {
            ForEach(Array(content.enumerated()), id: \.1.id) { index, content in
                if let url = content.imageURL {
                    HStack {
                        KFImage(URL(string: url))
                            .resizable()
                            .scaledToFill()
                            .frame(height: 300)
                        Spacer()
                    }.padding(5).tag(index)
                } else if let url = content.videoURL {
                    HStack {
                        VideoWrapper(url: url).frame(height: 300)
                        Spacer()
                    }.padding(5).tag(index)
                }
            }
        }
        .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always)).frame(height: 310)
    }
}

struct VideoWrapper: View {
    let url: URL
    @State var player = AVPlayer(url: URL(string: "https://www.google.com")!)
    @State var viewID = false

    var body: some View {
        ZStack {
            VideoPlayer(player: $player).scaledToFill().id(viewID)
        }
        .onAppear {
            player = AVPlayer(url: url)
            player.isMuted = false
            viewID.toggle()
            self.player.play()
        }
        .onDisappear {
            self.player.pause()
        }
    }
}


//not important for question

struct VideoPlayer : UIViewControllerRepresentable {
    @Binding var player : AVPlayer
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<VideoPlayer>) -> AVPlayerViewController {

        let controller = AVPlayerViewController()
        controller.player = player
        controller.showsPlaybackControls = false
        controller.allowsVideoFrameAnalysis = false
        return controller
    }
    
    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<VideoPlayer>) { }
}
struct uploadContent: Identifiable {
    var id: UUID = UUID()
    var isImage: Bool
    var videoURL: URL?
    var imageURL: String?
}

推荐答案

我做了一些调整.最重要的一点是,当TabView改变页面时,不一定会调用onAppearonDisappear.相反,你可以依靠selection来判断它是否是当前页面.然后,我根据该 Select 状态加载AVPlayer.

请参见内联注释.

struct MainView: View {
    @State private var selection = 0
    @State var content: [uploadContent] = [uploadContent(isImage: false, videoURL: URL(string: "https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/stories%2F73F8CF05-39AB-4A8F-AAEF-C70C9A57B22D.mp4?alt=media&token=5b5a63e3-e5af-4e2f-84c4-416abd554b84")!),
                                           uploadContent(isImage: true, imageURL: "https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/messages%2F19EAAFB0-0AC1-4100-88DC-F9980531D559?alt=media&token=5b206605-94e2-43f3-9dfb-1922cfecc989"),
                                           uploadContent(isImage: false, videoURL: URL(string: "https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/stories%2F73F8CF05-39AB-4A8F-AAEF-C70C9A57B22D.mp4?alt=media&token=5b5a63e3-e5af-4e2f-84c4-416abd554b84")!),
                                           uploadContent(isImage: false, videoURL: URL(string: "https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/stories%2F73F8CF05-39AB-4A8F-AAEF-C70C9A57B22D.mp4?alt=media&token=5b5a63e3-e5af-4e2f-84c4-416abd554b84")!),
                                           uploadContent(isImage: true, imageURL: "https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/messages%2F01659DB0-C97D-4FB3-A0B4-0F0302FA463B?alt=media&token=9e5b0553-c081-443e-a263-7e0e81630281")]
    
    var body: some View {
        TabView(selection: $selection) {
            ForEach(Array(content.enumerated()), id: \.1.id) { index, content in
                Group {
                    if let url = content.imageURL {
                        HStack {
                            AsyncImage(url: (URL(string: url)))
                                .scaledToFill()
                                .frame(height: 300)
                            Spacer()
                        }
                    } else if let url = content.videoURL {
                        HStack {
                            VideoWrapper(url: url,
                                         isSelected: selection == index)
                            .frame(height: 300)
                            Spacer()
                        }
                    }
                }
                .padding(5)
                .tag(index)
            }
        }
        .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always)).frame(height: 310)
    }
}

struct VideoWrapper: View {
    let url: URL
    let isSelected: Bool
    
    @State private var player: AVPlayer? // <-- Don't bother loading with an incorrect value
    
    var body: some View {
        ZStack {
            if let player {
                VideoPlayer(player: $player)
                    .scaledToFill()
            }
        }
        .task(id: isSelected) {
            if isSelected {
                if player == nil {
                    player = AVPlayer(url: url) // <-- Load the player if necessary
                    player?.isMuted = false
                }
                await self.player?.seek(to: .zero) // <-- Always rewind
                self.player?.play() // <-- Start
            } else {
                self.player?.pause() // <-- pause when we're off screen
            }
        }
    }
}


//not important for question

struct VideoPlayer : UIViewControllerRepresentable {
    @Binding var player : AVPlayer? // <-- now is Optional to match the parent view
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<VideoPlayer>) -> AVPlayerViewController {
        
        let controller = AVPlayerViewController()
        controller.player = player
        controller.showsPlaybackControls = false
        controller.allowsVideoFrameAnalysis = false
        return controller
    }
    
    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<VideoPlayer>) { }
}

Swift相关问答推荐

TabView中的视频播放器意外播放

try 在SwiftUI中做5星评级

按变换zoom 在UIView中不起作用

如何获取嵌套数组中项的路径以修改数组?

使用索引来访问 libc 定义的元组

RealityKit - 从中心zoom 模型

使第二个视图变灰 SwiftUI

如何返回一个通过 SwiftUI 中的另一个协议间接继承 View 协议的 struct ?

Swift // Sprite Kit:类别位掩码限制

XCUITest 在 TearDown 期间随机失败-无法终止 com.bundle.id

与 SwiftUI 中的 onChange 修饰符相比,objectWillChangeSequence 的目的是什么?

令人满意的 var body:协议扩展中的一些视图

我如何从 UIAlertController 导航到新屏幕(swiftUI)

What is "SwiftPM.SPMRepositoryError error 5"?

如何在不阻塞 UI 更新的情况下使用 Swift Concurrency 在后台执行 CPU 密集型任务?

找不到接受提供的参数的/的重载

当我必须在 Swift 中的类、 struct 和枚举之间进行 Select 时,我应该如何推理?

FCM 后台通知在 iOS 中不起作用

在 Swiftui 中是否有一种简单的方法可以通过捏合来放大图像?

应用程序包不包含有效标识符