Make A Custom Play Button For A macOS SwiftUI Video Player
The default VideoPlayer in macOS SwiftUI tries to scrub left and right if your mouse is over it and you try to scroll up and down. I'm getting around that by removing the native controls and adding my own play button.
import AVKit
import SwiftUI
struct ContentView: View {
@State var player = AVPlayer(url: Bundle.main.url(forResource: "example-video", withExtension: "mp4")!)
@State var isPlaying: Bool = false
let playerDidFinishNotification = NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime)
var body: some View {
ScrollView {
VStack {
AVPlayerControllerRepresented(player: player)
.frame(width: 400, height: 260)
.disabled(true)
Button {
if isPlaying == true {
player.pause()
} else {
player.seek(to: .zero)
player.play()
}
isPlaying.toggle()
} label: {
Image(systemName: isPlaying ? "stop" : "play")
.padding()
}.onReceive(playerDidFinishNotification, perform: { _ in
isPlaying = false
})
}
}.padding()
}
}
struct AVPlayerControllerRepresented : NSViewRepresentable {
var player : AVPlayer
func makeNSView(context: Context) -> AVPlayerView {
let view = AVPlayerView()
view.controlsStyle = .none
view.player = player
return view
}
func updateNSView(_ nsView: AVPlayerView, context: Context) {
}
}
#Preview {
ContentView()
}
TODO
Add in something like this code which checks to make sure the video file is there (most of the time it's probably not as necessary if you're bundling the videos directly. Still seems like a good backstop to have in place)
if let url = Bundle.main.url(forResource: "\(exampleItem.basename)-video", withExtension: "mp4") {
VideoPlayer(player: AVPlayer(url: url))
// .containerRelativeFrame(.vertical, count: 5, span: 2, spacing: 2)
// .ignoresSafeArea()
// .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.frame(height: 280)
.padding()
} else {
Text("No video")
}
Endnotes
I don't need scrubbing in the video for my current task of building AutoTyper. There will be times when I do though and this approach would need to have a more complete set of controlls added (or another approach would need to be used)
I'd also want pause as well as stop/start-over if the clips I was working with were longer. The basics in place for this approach should provide a good start for that
References
This is the main one that I used for reference.
This shows a basic play/stop button but the button doesn't change back from stop to play when the end of the video is reached.
This is where I found the sample to get started with switching the play/stop button at the end of the video.
No real help here. Something to investigate for the future to see what else is available
This is about an older version of iOS, but the .disabled(true)
was a final piece to prevent the default scrubbing behavior when the scroll wheel is used on the mouse while over the video