【问题标题】:Change AVPlayer source in SwiftUI?在 SwiftUI 中更改 AVPlayer 源?
【发布时间】: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,但不会改变播放器的视频/来源!

我还有什么需要做的吗?

【问题讨论】:

    标签: swiftui


    【解决方案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")!)我用于测试目的并使用本地视频。
    • 有一个问题,我不知道如何解决。加载新视频时,它不会循环播放。基本上通知变得无用,视频只播放一次。
    • 你知道为什么如果源发生变化视频停止循环吗?
    • 尚未检查,将检查
    猜你喜欢
    • 2020-09-15
    • 1970-01-01
    • 2020-03-05
    • 1970-01-01
    • 2021-03-24
    • 2020-06-14
    • 2021-08-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多