如何在SwiftUI中管理AVPlayer状态 [英] How to manage AVPlayer state in SwiftUI
问题描述
我在SwiftUI中有一个URL列表.当我点击一个项目时,我会显示一个全屏视频播放器.我有一个 @EnvironmentObject
处理一些查看器选项(例如,是否显示时间码).我还具有一个显示和隐藏时间码的切换开关(在此示例中仅包括了该切换开关,因为时间码视图无关紧要),但是每次更改该切换开关时,都会再次创建该视图,从而重新设置了 AVPlayer
.这是有道理的,因为我是在视图的初始化程序中创建播放器的.
I have a list of URLs in SwiftUI. When I tap an item, I present a full screen video player. I have an @EnvironmentObject
that handles some viewer options (for example, whether to show a timecode). I also have a toggle that shows and hides the timecode (I've only included the toggle in this example as the timecode view doesn't matter) but every time I change the toggle the view is created again, which re-sets the AVPlayer
. This makes sense since I'm creating the player in the view's initialiser.
我曾考虑过创建自己的 ObserveredObject
类以包含 AVPlayer
,但是我不确定如何或在何处对其进行初始化,因为我需要给它一个URL,我只从 CustomPlayerView
的初始化程序知道.我还考虑过将播放器设置为 @EnvironmentObject
,但是似乎很奇怪地初始化了一些我可能不需要的东西(如果用户没有点击URL来启动播放器).
I thought about creating my own ObserveredObject
class to contain an AVPlayer
but I'm not sure how or where I'd initialise it since I need to give it a URL, which I only know from the initialiser of CustomPlayerView
. I also thought about setting the player as an @EnvironmentObject
but it seems weird to initialise something I might not need (if the user doesn't tap on a URL to start the player).
请创建创建 AVPlayer
交给AVKit的 VideoPlayer
的正确方法是什么?这是我的示例代码:
What is the correct way to create an AVPlayer
to hand to AVKit's VideoPlayer
please? Here's my example code:
class ViewerOptions: ObservableObject {
@Published var showTimecode = false
}
struct CustomPlayerView: View {
@EnvironmentObject var viewerOptions: ViewerOptions
private let avPlayer: AVPlayer
init(url: URL) {
avPlayer = AVPlayer(url: url)
}
var body: some View {
HStack {
VideoPlayer(player: avPlayer)
Toggle(isOn: $viewerOptions.showTimecode) { Text("Show Timecode") }
}
}
}
推荐答案
您可以在此处采用两种方法.您可以尝试一下,看看哪个最适合您.
There are a couple of approaches you can take here. You can try them out and see which one suits best for you.
选项1:如您所说,您可以将 avPlayer
包装在新的 ObserveredObject
类
Option 1: As you said you can wrap avPlayer
in a new ObserveredObject
class
class PlayerViewModel: ObservableObject {
@Published var avPlayer: AVPlayer? = nil
}
class ViewerOptions: ObservableObject {
@Published var showTimecode = false
}
@main
struct DemoApp: App {
var playerViewModel = PlayerViewModel()
var viewerOptions = ViewerOptions()
var body: some Scene {
WindowGroup {
CustomPlayerView(url: URL(string: "Your URL here")!)
.environmentObject(playerViewModel)
.environmentObject(viewerOptions)
}
}
}
struct CustomPlayerView: View {
@EnvironmentObject var viewerOptions: ViewerOptions
@EnvironmentObject var playerViewModel: PlayerViewModel
init(url: URL) {
if playerViewModel.avPlayer == nil {
playerViewModel.avPlayer = AVPlayer(url: url)
} else {
playerViewModel.avPlayer?.pause()
playerViewModel.avPlayer?.replaceCurrentItem(with: AVPlayerItem(url: url))
}
}
var body: some View {
HStack {
VideoPlayer(player: playerViewModel.avPlayer)
Toggle(isOn: $viewerOptions.showTimecode) { Text("Show Timecode") }
}
}
}
选项2:您可以将 avPlayer
作为可选属性添加到现有类 ViewerOptions
中,然后在需要时对其进行初始化
Option 2: You can add avPlayer
to your already existing class ViewerOptions
as an optional property and then initialise it when you need it
class ViewerOptions: ObservableObject {
@Published var showTimecode = false
@Published var avPlayer: AVPlayer? = nil
}
struct CustomPlayerView: View {
@EnvironmentObject var viewerOptions: ViewerOptions
init(url: URL) {
if viewerOptions.avPlayer == nil {
viewerOptions.avPlayer = AVPlayer(url: url)
} else {
viewerOptions.avPlayer?.pause()
viewerOptions.avPlayer?.replaceCurrentItem(with: AVPlayerItem(url: url))
}
}
var body: some View {
HStack {
VideoPlayer(player: viewerOptions.avPlayer)
Toggle(isOn: $viewerOptions.showTimecode) { Text("Show Timecode") }
}
}
}
选项3:通过这种方式将您的 avPlayer
设置为状态对象,系统将对其内存进行管理,并且在您的视图存在之前,它将不会对其进行重新设置并将其保持活动状态.
Option 3: Make your avPlayer
a state object this way its memory will be managed by the system and it will not re-set it and keep it alive for you until your view exists.
class ViewerOptions: ObservableObject {
@Published var showTimecode = false
}
struct CustomPlayerView: View {
@EnvironmentObject var viewerOptions: ViewerOptions
@State private var avPlayer: AVPlayer
init(url: URL) {
_avPlayer = .init(wrappedValue: AVPlayer(url: url))
}
var body: some View {
HStack {
VideoPlayer(player: avPlayer)
Toggle(isOn: $viewerOptions.showTimecode) { Text("Show Timecode") }
}
}
}
选项4:在需要时创建您的 avPlayer
对象,然后忘记它(不确定这对您来说是最好的方法,但是如果您不需要播放器对象执行自定义操作,则可以使用此选项)
Option 4: Create your avPlayer
object when you need it and forget it (Not sure this is the best approach for you but if you do not need your player object to perform custom actions then you can use this option)
class ViewerOptions: ObservableObject {
@Published var showTimecode = false
}
struct CustomPlayerView: View {
@EnvironmentObject var viewerOptions: ViewerOptions
private let url: URL
init(url: URL) {
self.url = url
}
var body: some View {
HStack {
VideoPlayer(player: AVPlayer(url: url))
Toggle(isOn: $viewerOptions.showTimecode) { Text("Show Timecode") }
}
}
}
这篇关于如何在SwiftUI中管理AVPlayer状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!