在 SwiftUI 中更改 AVPlayer 源?

Posted

技术标签:

【中文标题】在 SwiftUI 中更改 AVPlayer 源?【英文标题】:Change AVPlayer source in SwiftUI? 【发布时间】:2021-02-04 11:56:23 【问题描述】:

我有一个 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
    

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

var body: some View 
    PlayerView()
    .edgesIgnoringSafeArea(.all)

我需要更改按钮单击/点按时的视频,所以我尝试了这个:

 .onTapGesture 

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

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

   player.play()
                                        
   

以上代码将重启player,但不会改变播放器的视频/来源!

我还有什么需要做的吗?

【问题讨论】:

【参考方案1】:

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

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()
        
    

现在,使用@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()
    

最后,在内容视图中创建 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)
    

【讨论】:

这个需要本地网址吗:AVPlayer(url: Bundle.main.url(forResource: "https://www.w3schools.com/html/movie.mp4", withExtension: "mp4")!) 没有。你可以直接使用AVPlayer(url: URL(string: "https://media.w3.org/2010/05/sintel/trailer.mp4")!)我用于测试目的并使用本地视频。 有一个问题,我不知道如何解决。加载新视频时,它不会循环播放。基本上通知变得无用,视频只播放一次。 你知道为什么如果源发生变化视频停止循环吗? 尚未检查,将检查

以上是关于在 SwiftUI 中更改 AVPlayer 源?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 SwiftUI 在 VStack 中显示我的 AVPlayer

SwiftUI VideoPlayer,更改默认黑色背景/侧面空间

如何根据布尔值 (SwiftUI) 更改对象的绑定源?

SwiftUI MacOS App AVPlayer 不适用于视频 URL

swiftui dealloc 和 realloc AVplayer 有很多视频

在 UITableView 中更改 AVPlayer 的 playerItem