如何在SwiftUI中管理AVPlayer状态 [英] How to manage AVPlayer state in SwiftUI

查看:76
本文介绍了如何在SwiftUI中管理AVPlayer状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆