I am trying to create a view that allows me to send a URL of the video I want to play. My view looks like this:
import SwiftUI
import AVKit
struct PlayerView: View {
var videoURL : String
private let player = AVPlayer(url: URL(string: "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8")!)
var body: some View {
VideoPlayer(player: player)
.onAppear() {
// Start the player going, otherwise controls don't appear
player.play()
}
.onDisappear() {
// Stop the player when the view disappears
player.pause()
}
}
}
If try to set the URL to the passed parameter I get this error:
Cannot use instance member 'videoURL' within property initializer; property initializers run before 'self' is available
How do I pass aURL to the View to play different movies?
I changed my code as suggested and the video does not play:
import SwiftUI
import AVKit
struct PlayerView: View {
var videoURL : String
@State private var player : AVPlayer?
var body: some View {
VideoPlayer(player: player)
.onAppear() {
// Start the player going, otherwise controls don't appear
guard let url = URL(string: videoURL) else {
return
}
print(url)
let player = AVPlayer(url: url)
self.player = player
self.player?.seek(to: CMTime.zero)
self.player!.play()
}
.onDisappear() {
// Stop the player when the view disappears
player?.pause()
}
}
}
I am trying to change the video on the timer. I tried this:
} else {
VideoPlayer(player: viewModel.player)
.onAppear {
viewModel.urlString = "https://testsite.neumont.edu/images/green.mp4"
}
.onReceive(movieSwitchTimer) { _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
viewModel.urlString = "https://testsite.neumont.edu/images/earth.mp4"
}
self.activeImageIndex = (self.activeImageIndex + 1) % slideStore.images.count
}
}
There are a number of ways to achieve this, but since you're already using onAppear
, that seems like a good place to accomplish this:
struct PlayerView: View {
var videoURL : String
@State private var player : AVPlayer?
var body: some View {
VideoPlayer(player: player)
.onAppear() {
// Start the player going, otherwise controls don't appear
guard let url = URL(string: videoURL) else {
return
}
let player = AVPlayer(url: url)
self.player = player
player.play()
}
.onDisappear() {
// Stop the player when the view disappears
player?.pause()
}
}
}
You'll want player
to be @State
because it should persist between renders of the view.
Update, based on comments, handling a scenario where the URL may change:
class VideoViewModel : ObservableObject {
@Published var urlString : String? {
didSet {
guard let urlString = urlString, let url = URL(string: urlString) else {
return
}
player = AVPlayer(url: url)
player.seek(to: .zero)
player.play()
}
}
var player = AVPlayer()
}
struct ContentView : View {
@StateObject var viewModel = VideoViewModel()
var body: some View {
VideoPlayer(player: viewModel.player)
.onAppear {
viewModel.urlString = "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8"
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
viewModel.urlString = Bundle.main.url(forResource: "IMG_0226", withExtension: "mp4")!.absoluteString
}
}
}
}
In the update, you can see that I store the player in an ObservableObject
. Any time the url string gets changed, it reloads the player. The onAppear
is not necessary in your code -- it's just a way to show loading different videos at different times (the first being the URL you provided, the second being a URL from my bundle).