What is best practice for wrapping AVPlayerView
in SwiftUI
?
I want to wrap AVPlayerView into SwiftUI. Here are my codes(playground):
import PlaygroundSupport
import SwiftUI
import AVKit
class RecorderPlayerModel: ObservableObject {
@Published var playerView: AVPlayerView
init() {
self.playerView = AVPlayerView()
self.playerView.player = AVPlayer()
}
func reload(url: URL) {
let asset = AVAsset(url: url)
let item = AVPlayerItem(asset: asset)
self.playerView.player?.replaceCurrentItem(with: item)
}
}
struct RecorderPlayerView: NSViewRepresentable {
typealias NSViewType = AVPlayerView
var playerView: AVPlayerView
func makeNSView(context: Context) -> AVPlayerView {
return playerView
}
func updateNSView(_ nsView: AVPlayerView, context: Context) {}
}
struct ContentView: View {
@StateObject var playerViewModel: RecorderPlayerModel = .init()
var body: some View {
VStack {
RecorderPlayerView(playerView: playerViewModel.playerView)
.clipShape(RoundedRectangle(cornerRadius: 8))
.onAppear {
let fileManager = FileManager.default
if let url = URL(string: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerMeltdowns.mp4") {
Task {
do {
let (data, _) = try await URLSession.shared.data(from: url)
let fileUrl = fileManager.temporaryDirectory.appendingPathComponent("sample")
.appendingPathExtension(for: .mpeg4Movie)
try? fileManager.removeItem(at: fileUrl)
fileManager.createFile(atPath: fileUrl.path, contents: data)
playerViewModel.reload(url: fileUrl)
} catch {
print(error)
}
}
}
}
Button {
if playerViewModel.playerView.canBeginTrimming {
Task {
await playerViewModel.playerView.beginTrimming()
}
}
} label: {
Text("trim")
}
}.frame(width: 500, height: 500, alignment: .center)
}
}
PlaygroundPage.current.setLiveView(ContentView())
Since I want to trim the video, I cannot directly use VideoPlayer. But after wrapping AVPlayerView to NSViewRepresentable View, the trim view always lose interactivity.
Reproduce way: just double click at anywhere when trimming.
I finally figure out the problem. It seems that applying .clipShape(...)
on RecorderPlayerView
will cause the problem.
Just remove the line .clipShape(RoundedRectangle(cornerRadius: 8))
will solve this problem.
RecorderPlayerView(playerView: playerViewModel.playerView)
// .clipShape(RoundedRectangle(cornerRadius: 8)) <-- remove this line
.onAppear {
...
}
Maybe there are some bugs in .clipShape
and AVPlayerView
in trimming mode.