Trouble with Playing Videos in SwiftUI using AVPlayer; I can listen but not see

61 Views Asked by At

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!

0

There are 0 best solutions below