SwiftUI:VideoPlayer 显示本地存储的不同视频(后退和前进按钮)

SwiftUI: VideoPlayer to display different videos stored locally (back and forward buttons)

我还是 SwiftUI 的新手,遇到了 运行 问题。我需要使用后退和前进按钮让视频播放器转到 previous/next 视频(本地存储)。以下代码仅适用于一个视频,即在 init() 中声明的那个视频,但我无法通过单击后退和前进按钮来更改视频。 我正在使用一个名为 videoNames 的字符串数组来传递上一个视图中的所有视频名称。 此外,我为此使用了自定义视频播放器,我将包含代码的相关部分。

这是我的观点:

struct WorkingOutSessionView: View {

let videoNames: [String]

@State var customPlayer : AVPlayer
@State var isplaying = false
@State var showcontrols = false

init(videoNames: [String]) {
    self.videoNames = videoNames
    self._customPlayer = State(initialValue: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: videoNames[0], ofType: "mov")!)))
}

var body: some View {
    VStack {
        CustomVideoPlayer(player: $customPlayer)
            .frame(width: 390, height: 219)
            .onTapGesture {
                self.showcontrols = true
            }
        
        GeometryReader {_ in
            
            // BUTTONS
            HStack {
                
                // BACK BUTTON
                Button(action: {
                    // code
                }, label: {
                    Image(systemName: "lessthan")
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 60, height: 60)
                        .foregroundColor((Color(red: 243/255, green: 189/255, blue: 126/255)))
                        .padding()
                })
                
                // FORWARD BUTTON
                Button(action: {
                    // code
                }, label: {
                    Image(systemName: "greaterthan")
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 60, height: 60)
                        .foregroundColor((Color(red: 243/255, green: 189/255, blue: 126/255)))
                        .padding()
                })
            }
        }
    }
    .offset(y: 35)
    .edgesIgnoringSafeArea(.all)
}

这是我的自定义视频播放器:

struct CustomVideoPlayer : UIViewControllerRepresentable {

     @Binding var player: AVPlayer

     func makeUIViewController(context: UIViewControllerRepresentableContext<CustomVideoPlayer>) -> AVPlayerViewController {
      
         let controller = AVPlayerViewController()
         controller.player = player
         controller.showsPlaybackControls = false
         return controller
     }

     func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<CustomVideoPlayer>) {
     }    
}

我已经研究了解决方案,但找不到任何相关内容。我试图修改我的 CustomVideoPlayer 并且不传递 @Binding 变量...同样, init() 也让我很头疼,因为每次我更改某些内容时它都会返回错误... 任何解决方案都会对大家有所帮助。非常感谢您的宝贵时间。

您需要做的第一件事是跟踪您在视频列表中的位置。我为此使用了另一个 @State 变量。

只要该状态变量发生变化,您就需要更新您的播放器。我在代码底部附近使用 onChange 修饰符来完成这项工作。

在您的 CustomVideoPlayer 中,您需要使用 updateUIViewController 来确保 player 与传入的参数保持同步。

最后,AVPlayer 不需要是 @Binding,因为它是按引用传递的 class,而不是按值传递的结构。


struct WorkingOutSessionView: View {
    
    let videoNames: [String]
    
    @State private var customPlayer : AVPlayer
    @State private var currentItem = 0
    @State var isplaying = false
    @State var showcontrols = false
    
    init(videoNames: [String]) {
        self.videoNames = videoNames
        self._customPlayer = State(initialValue: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: videoNames[0], ofType: "mov")!)))
    }
    
    var body: some View {
        VStack {
            CustomVideoPlayer(player: customPlayer)
                .frame(width: 390, height: 219)
                .onTapGesture {
                    self.showcontrols = true
                }
                .onAppear {
                    self.customPlayer.play()
                }
            
            GeometryReader { _ in
                
                // BUTTONS
                HStack {
                    // BACK BUTTON
                    Button(action: {
                        currentItem = min(currentItem, currentItem - 1)
                    }) {
                        Image(systemName: "lessthan")
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 60, height: 60)
                            .foregroundColor((Color(red: 243/255, green: 189/255, blue: 126/255)))
                            .padding()
                    }
                    
                    // FORWARD BUTTON
                    Button(action: {
                        currentItem = min(videoNames.count - 1, currentItem + 1)
                    }) {
                        Image(systemName: "greaterthan")
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 60, height: 60)
                            .foregroundColor((Color(red: 243/255, green: 189/255, blue: 126/255)))
                            .padding()
                    }
                }
            }
        }
        .offset(y: 35)
        .edgesIgnoringSafeArea(.all)
        .onChange(of: currentItem) { currentItem in
            print("Going to:",currentItem)
            self.customPlayer.pause()
            self.customPlayer = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: videoNames[currentItem], ofType: "mov")!))
            self.customPlayer.play()
        }
    }
}
    

struct CustomVideoPlayer : UIViewControllerRepresentable {
     var player: AVPlayer

     func makeUIViewController(context: UIViewControllerRepresentableContext<CustomVideoPlayer>) -> AVPlayerViewController {
      
         let controller = AVPlayerViewController()
         controller.player = player
         controller.showsPlaybackControls = false
         return controller
     }

     func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<CustomVideoPlayer>) {
        uiViewController.player = player
     }
}

如果这是我自己的项目,我可能会继续进行一些重构——也许将一些东西移动到视图模型等。此外,我可能会避免在View's init 正如我在上次对你上一个问题的回答中提到的那样。它可以工作,但如果视图层次结构重新呈现,您肯定会有做太多繁重工作的风险。