I'm working on a SwiftUI project where I need to play videos using AVPlayer. However, I'm encountering some difficulties with my implementation. Here's a summary of what I'm trying to achieve:
I have a list of videos fetched from a JSON file. Each video item in the list has a play button. When the play button is tapped, I want the video to play in a fullscreen AVPlayerViewController. Here's the code I'm using:
import SwiftUI
import AVKit
struct AVPlayerViewControllerRepresentable: UIViewControllerRepresentable {
let player: AVPlayer
func makeUIViewController(context: Context) -> AVPlayerViewController {
let viewController = AVPlayerViewController()
viewController.player = player
return viewController
}
func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {
print("AVPlayerViewController updated")
print("Frame: \(uiViewController.view.frame)")
print("Superview Frame: \(uiViewController.view.superview?.frame ?? CGRect.zero)")
print("IsHidden: \(uiViewController.view.isHidden)")
}
}
struct VideoPlayerView: UIViewControllerRepresentable {
let player: AVPlayer
func makeUIViewController(context: Context) -> AVPlayerViewController {
let playerViewController = AVPlayerViewController()
playerViewController.player = player
return playerViewController
}
func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {
}
}
struct VideoView: View {
let video: VideoData
@State private var player = AVPlayer()
var body: some View {
Button(action: playVideo) {
Image(systemName: "play.circle")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50)
}
.sheet(isPresented: $isPresentingPlayer) {
AVPlayerViewControllerRepresentable(player: player)
.onDisappear {
player.pause()
}
}
}
func playVideo() {
guard let videoURL = Bundle.main.url(forResource: video.videoPath, withExtension: nil) else {
print("Video URL not found for: \(video.videoPath)")
return
}
player = AVPlayer(url: videoURL)
player.play()
let playerViewController = AVPlayerViewController()
playerViewController.player = player
if let scene = UIApplication.shared.connectedScenes.first(where: { $0 is UIWindowScene }) as? UIWindowScene {
guard let window = scene.windows.first(where: { $0.isKeyWindow }) else { return }
window.rootViewController?.present(playerViewController, animated: true, completion: nil)
}
isPresentingPlayer = true
}
@State private var isPresentingPlayer = false
}
struct PlayView: View {
var body: some View {
VStack(alignment: .leading) {
Text("Marriott®")
.font(.largeTitle)
.fontWeight(.bold)
.padding(20)
ModuleContentView(title: "Roleplay Exercises", description: "Test your knowledge using the Marriott Roleplay Exercises on your Apple Vision Pro.") {
VideoListView()
}
.padding(.horizontal, 20)
}
}
}
struct VideoListView: View {
@State private var videos: [VideoData] = []
var body: some View {
ScrollView(.horizontal) {
HStack(spacing: 20) {
ForEach(videos) { video in
VideoItemView(video: video)
}
}
.padding()
}
.onAppear {
loadData()
}
}
func loadData() {
if let fileURL = Bundle.main.url(forResource: "videoList", withExtension: "json") {
do {
let data = try Data(contentsOf: fileURL)
videos = try JSONDecoder().decode([VideoData].self, from: data)
} catch {
print("Error loading JSON data: \(error)")
}
}
}
}
struct VideoItemView: View {
let video: VideoData
var body: some View {
VStack(alignment: .leading) {
VideoView(video: video)
.padding(20)
Text(video.title)
.font(.headline)
}
.padding()
.frame(width:200, height: 200)
.background(Color.gray.opacity(0.2))
}
}
struct ModuleContentView<Content: View>: View {
let title: String
let description: String
let content: () -> Content
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text(title)
.font(.title)
.fontWeight(.bold)
Text(description)
.font(.body)
content()
}
}
}
struct MyContentView: View {
@StateObject var navigationEnvironment = NavigationEnvironmentObject()
@State private var videos: [VideoData] = []
var body: some View {
NavigationView {
List(videos) { video in
VideoView(video: video)
.environmentObject(navigationEnvironment)
}
.navigationTitle("My Videos")
}
.environmentObject(navigationEnvironment)
.onAppear {
loadData()
}
}
private func loadData() {
if let fileURL = Bundle.main.url(forResource: "videoList", withExtension: "json") {
do {
let data = try Data(contentsOf: fileURL)
videos = try JSONDecoder().decode([VideoData].self, from: data)
} catch {
print("Error loading JSON data: \(error)")
}
}
}
}
struct VideoData: Codable, Identifiable {
let id: Int
let title: String
let videoPath: String
let completion: Float
}
class NavigationEnvironmentObject: ObservableObject {}
#if DEBUG
struct PlayView_Previews: PreviewProvider {
static var previews: some View {
PlayView()
}
}
#endif
The issue I'm facing is that when I tap the play button, the video doesn't play as expected. Instead, I'm encountering a white screen.
I've tried changing the UI positions, but I'm still unable to resolve the problem.
Could someone please review my code and provide guidance on how to fix this issue? Any help would be greatly appreciated!