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 正如我在上次对你上一个问题的回答中提到的那样。它可以工作,但如果视图层次结构重新呈现,您肯定会有做太多繁重工作的风险。
我还是 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 正如我在上次对你上一个问题的回答中提到的那样。它可以工作,但如果视图层次结构重新呈现,您肯定会有做太多繁重工作的风险。