Reputation: 7012
Using Swift5.3.2, iOS14.4.1, Xcode 12.4,
I am successfully running a VideoPlayer in SwiftUI.
I am calling the Player view with this code: VideoPlayer(player: AVPlayer(url: url))
.
The problem is that the video stops playing whenever a parent-View of the VideoPlayer updates (i.e. re-renders).
Since in SwiftUI I don't have any control over when such a re-render moment takes place, I don't know how to overcome this problem.
Any ideas ?
Here is the entire Code:
The VideoPlayer View is called as such:
struct MediaTabView: View {
@State private var url: URL
var body: some View {
// CALL TO VIDEOPLAYER IS HERE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
VideoPlayer(player: AVPlayer(url: url))
}
}
The MediaTabView is called as such:
import SwiftUI
struct PageViewiOS: View {
var body: some View {
ZStack {
Color.black
// CALL TO MEDIATABVIEW IS HERE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!d
MediaTabView(url: URL(string: "https://someurel.com"))
CloseButtonView()
}
}
}
The PageViewiOS View is called as such:
struct MainView: View {
@EnvironmentObject var someState: AppStateService
var body: some View {
NavigationView {
Group {
if someState = .stateOne {
// CALL TO PAGEVIEWIOS IS HERE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
PageViewiOS()
} else {
Text("hello")
}
}
}
}
}
Upvotes: 0
Views: 2355
Reputation: 1
I have done a simplified version of the accepted answer without the need to have a view model.
private struct CustomPlayerView: View {
@State private var avPlayer: AVPlayer
init(url: URL) {
_avPlayer = State(initialValue: AVPlayer(url: url))
}
var body: some View {
VideoPlayer(player: avPlayer)
}
}
which can be used in the same way.
Upvotes: 0
Reputation: 7012
With the solution from @jnpdx, everything works now.
Here is the final solution (full credit to @jnpdx):
import SwiftUI
import AVKit
class PlayerViewModel: ObservableObject {
@Published var avPlayer: AVPlayer?
func loadFromUrl(url: URL) {
avPlayer = AVPlayer(url: url)
}
}
struct CustomPlayerView: View {
var url : URL
@StateObject private var playerViewModel = PlayerViewModel()
var body: some View {
ZStack {
if let avPlayer = playerViewModel.avPlayer {
VideoPlayer(player: avPlayer)
}
}.onAppear {
playerViewModel.loadFromUrl(url: url)
}
}
}
With that in hand, it is enough to call the CustomPlayerVideo like that:
CustomPlayerView(url: url)
Remark: I needed to use ZStack
instead of Group
in my CustomPlayerView in order for it to work.
Upvotes: 2
Reputation: 52565
This is in response to our comment thread on the other answer:
class PlayerViewModel: ObservableObject {
@Published var avPlayer: AVPlayer?
func loadFromUrl(url: URL) {
avPlayer = AVPlayer(url: url)
}
}
struct CustomPlayerView: View {
var url : URL
@StateObject private var playerViewModel = PlayerViewModel()
var body: some View {
ZStack {
if let avPlayer = playerViewModel.avPlayer {
VideoPlayer(player: avPlayer)
}
}.onAppear {
playerViewModel.loadFromUrl(url: url)
}
}
}
I'm not sure that this is definitively better, so it's worth testing. But, it does control when AVPlayer
gets created and avoids re-creating PlayerViewModel
on every render of the parent as well.
Upvotes: 0
Reputation: 7012
I have found a solution.
Call the following :
CustomPlayerView(url: url)
...instead of :
VideoPlayer(player: AVPlayer(url: url))
Not sure why this works, tough.
Maybe somebody can explain further ?
Here is the CustomVideoPlayer code:
struct CustomPlayerView: View {
private let url: URL
init(url: URL) {
self.url = url
}
var body: some View {
VideoPlayer(player: AVPlayer(url: url))
}
}
With this minor change, the Video keeps on playing even tough the parent-View gets re-rendered. Still, not sure why ???
----------- Answer with the hint of @jnpdx --------
I changed the CustomVideoPlayer even more :
CustomPlayerView(playerViewModel: PlayerViewModel(avPlayer: AVPlayer(url: url)))
import SwiftUI
import AVKit
class PlayerViewModel: ObservableObject {
@Published var avPlayer: AVPlayer
init(avPlayer: AVPlayer) {
self.avPlayer = avPlayer
}
}
struct CustomPlayerView: View {
@StateObject var playerViewModel: PlayerViewModel
var body: some View {
VideoPlayer(player: playerViewModel.avPlayer)
}
}
Upvotes: 0