我有一个 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但它不会更改播放器的视频/源!
我还需要做些什么吗?
首先,创建一个适当的 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)