Reputation: 337
I am trying to use VLCKit along with AVPlayerLayer (in order to eventually use PIP) I have VLCKit working correctly but I am unable to use it with PlayerLayer
This is the most basic working example using AVPlayer as the player that I am trying to modify but with no luck. Any help appreciated, thanks.
import AVFoundation
import SwiftUI
class PlayerView: UIView {
var player: AVPlayer? {
get {
return playerLayer.player
}
set {
playerLayer.player = newValue
}
}
init(player: AVPlayer) {
super.init(frame: .zero)
self.player = player
self.backgroundColor = .black
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var playerLayer: AVPlayerLayer {
return layer as! AVPlayerLayer
}
// Override UIView property
override static var layerClass: AnyClass {
return AVPlayerLayer.self
}
}
struct PlayerContainerView: UIViewRepresentable {
typealias UIViewType = PlayerView
let player: AVPlayer
init(player: AVPlayer) {
self.player = player
}
func makeUIView(context: Context) -> PlayerView {
return PlayerView(player: player)
}
func updateUIView(_ uiView: PlayerView, context: Context) { }
}
class PlayerViewModel: ObservableObject {
let player: AVPlayer
init(fileName: String) {
let url = Bundle.main.url(forResource: fileName, withExtension: "mp4")
self.player = AVPlayer(playerItem: AVPlayerItem(url: url!))
}
@Published var isPlaying: Bool = false {
didSet {
if isPlaying {
play()
} else {
pause()
}
}
}
func play() {
let currentItem = player.currentItem
if currentItem?.currentTime() == currentItem?.duration {
currentItem?.seek(to: .zero, completionHandler: nil)
}
player.play()
}
func pause() {
player.pause()
}
}
enum PlayerAction {
case play
case pause
}
struct ContentView: View {
@ObservedObject var model: PlayerViewModel
init() {
model = PlayerViewModel(fileName: "video")
}
var body: some View {
ZStack {
VStack {
PlayerContainerView(player: model.player)
.frame(height: 200)
Button(action: {
self.model.isPlaying.toggle()
}, label: {
Image(systemName: self.model.isPlaying ? "pause" : "play")
.font(.largeTitle)
.padding()
})
}
}
.ignoresSafeArea()
}
}
Upvotes: 0
Views: 435
Reputation: 30727
In SwiftUI you can't have classes in your View
structs. So first rename PlayerViewModel
as PlayerCoordinator
and init it inside the UIViewRepresentable
. It is also essential you implement updateUIView
to copy the values from the struct to the objects, e.g. something like this:
class PlayerCoordinator {
let player: AVPlayer
...
struct PlayerContainerView: UIViewRepresentable {
@Binding var isPlaying: Bool
func makeCoordinator() -> Coordinator {
PlayerCoordinator()
}
func updateUIView(_ uiView: PlayerView, context: Context) {
context.coordinator.didPlay = nil
context.coordinator.didPause = nil
if isPlaying == true && context.coordinator.player.isPlayer == false {
context.coordinator.player.play()
}
if isPlaying == false && context.coordinator.player.isPlayer == true {
context.coordinator.player.pause()
}
coordinator.didPause = {
isPlaying = false
}
coordinator.didPlay = {
isPlaying = true
}
}
}
Then your View struct should be like this:
struct ContentView: View {
@State var isPlaying = false
var body: some View {
ZStack {
VStack {
PlayerView(isPlaying: isPlaying)
.frame(height: 200)
Button(action: {
isPlaying.toggle()
}, label: {
Image(systemName: isPlaying ? "pause" : "play")
.font(.largeTitle)
.padding()
})
}
}
.ignoresSafeArea()
}
}
Upvotes: 0