更改 SwiftUI 中的 AVPlayer 源?

roo*_*far 4 swiftui

我有一个 AVPlayer,可以在我的 SwiftUI 应用程序的后台播放视频,效果很好。

但我需要允许用户通过点击/单击按钮来更改视频。

这是我播放视频的代码:

var player = AVPlayer()
var bgVideoURL = "https://www.w3schools.com/html/movie.mp4"


struct PlayerView: UIViewRepresentable {
    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PlayerView>) {
    }

    func makeUIView(context: Context) -> UIView {
        return PlayerUIView(frame: .zero)
    }
}

class PlayerUIView: UIView {

    private let playerLayer = AVPlayerLayer()

    override init(frame: CGRect) {
        super.init(frame: frame)

        let url = URL(string: bgVideoURL)!
        player = AVPlayer(url: url)
        player.actionAtItemEnd = .none
        
        player.play()

        playerLayer.player = player
        playerLayer.videoGravity = .resizeAspectFill

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(playerItemDidReachEnd(notification:)),
                                               name: .AVPlayerItemDidPlayToEndTime,
                                               object: player.currentItem)

        layer.addSublayer(playerLayer)
    }
    
    

    @objc func playerItemDidReachEnd(notification: Notification) {
        if let playerItem = notification.object as? AVPlayerItem {
            playerItem.seek(to: .zero, completionHandler: nil)
        }
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        playerLayer.frame = bounds
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是我播放视频的方式:

var body: some View {
    PlayerView()
    .edgesIgnoringSafeArea(.all)
}
Run Code Online (Sandbox Code Playgroud)

我需要在按钮单击/点击时更改视频,所以我尝试了以下方法:

 .onTapGesture {

   player.pause()
   player.seek(to: .zero)

   bgVideoURL = "https://media.w3.org/2010/05/sintel/trailer.mp4"

   player.play()
                                        
   }
Run Code Online (Sandbox Code Playgroud)

上面的代码将重新启动,player但它不会更改播放器的视频/源!

我还需要做些什么吗?

Raj*_*han 5

首先,创建一个适当的 PlayerUIView 类(删除全局变量等)

玩家界面视图

class PlayerUIView: UIView {
    
    // MARK: Class Property
    
    let playerLayer = AVPlayerLayer()
    
    // MARK: Init
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    init(player: AVPlayer) {
        super.init(frame: .zero)
        self.playerSetup(player: player)
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    // MARK: Life-Cycle
    
    override func layoutSubviews() {
        super.layoutSubviews()
        playerLayer.frame = bounds
    }
    
    // MARK: Class Methods
    
    private func playerSetup(player: AVPlayer) {
        playerLayer.player = player
        player.actionAtItemEnd = .none
        layer.addSublayer(playerLayer)
        
        self.setObserver()
    }
    
    func setObserver() {
        NotificationCenter.default.removeObserver(self)
        NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd(notification:)),
                                               name: .AVPlayerItemDidPlayToEndTime,
                                               object: playerLayer.player?.currentItem)
    }
    
    @objc func playerItemDidReachEnd(notification: Notification) {
        if let playerItem = notification.object as? AVPlayerItem {
            playerItem.seek(to: .zero, completionHandler: nil)
            self.playerLayer.player?.play()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,用于@Binding在 PlayerView 中绑定您的播放器

玩家视图

struct PlayerView: UIViewRepresentable {
    
    @Binding var player: AVPlayer
    
    func makeUIView(context: Context) -> PlayerUIView {
        return PlayerUIView(player: player)
    }
    
    func updateUIView(_ uiView: PlayerUIView, context: UIViewRepresentableContext<PlayerView>) {
        uiView.playerLayer.player = player
        
        //Add player observer.
        uiView.setObserver()
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,在内容视图中创建 AVPlayer 对象,用 AVPlayer 更改新的 URL。

struct PlayerContentView: View {
    
    @State private var player = AVPlayer(url: URL(string: "https://media.w3.org/2010/05/sintel/trailer.mp4")!)
    
    var body: some View {
        PlayerView(player: $player)
            .onTapGesture {
                player.pause()
                player.seek(to: .zero)
                
                player = AVPlayer(url: Bundle.main.url(forResource: "temp_video", withExtension: "mp4")!) // or AVPlayer(url: URL(string: "https://media.w3.org/2010/05/sintel/trailer.mp4")!)
                player.play()
                
            }
            .onAppear {
                player.play()
            }
        .edgesIgnoringSafeArea(.all)
    }
}
Run Code Online (Sandbox Code Playgroud)