How to present video to full screen?

120 Views Asked by At

I have a video player in SwiftUI that is part of a subview and it is not at the top of the view hierarchy. The video player is inside of a scroll view with many other views. I would like to present this video in full screen when it is tapped. The link below is an example of the desired functionality. When the video is tapped it should resize to the full size of the screen and cover everything. I'm not sure how to do this since the video player is in a sub view.

example

https://drive.google.com/file/d/1fom7lDNjP7thxTuowYtsUXbErvX2jhzH/view?usp=sharing

This is an example from snapchat. Clicking the video animates it to full screen. When in full screen a drag gesture down animates it back to its normal size in the scroll view.

struct ContentView: View { //top of the view heiarchy
    
    var body: some View {
        SomeView()
    }
}

struct SomeView: View {
    let example = "example https://drive.google.com/file/d/1fom7lDNjP7thxTuowYtsUXbErvX2jhzH/view?usp=sharing"
    
    var body: some View {
        ScrollView {
            //some views
            
            VideoPlayerView(url: URL(string: example)!)
            
            //some views
        }
    }
}

struct VideoPlayerView: View {
    let url: URL
    @State var player = AVPlayer(url: URL(string: "https://www.google.com")!)
    @State var aspect: CGSize = CGSize(width: 16, height: 9)
    
    var body: some View {
        ZStack {
            VideoPlayer(player: $player, showControls: false)
                .aspectRatio(aspect, contentMode: .fit)
                .onTapGesture {
                   //enter full screen from here while still playing from same duration
                }
        }
        .onAppear {
            player = AVPlayer(url: url)
 
            Task {
                do {
                    if let final = try await getVideoResolution(url: url.absoluteString) {
                        self.aspect = final
                    }
                } catch { }
            }
            
            self.player.play()
        }
    }
}

func getVideoResolution(url: String) async throws -> CGSize? {
    guard let track = try await AVURLAsset(url: URL(string: url)!).loadTracks(withMediaType: AVMediaType.video).first else { return nil }
    let size = try await track.load(.naturalSize).applying(track.load(.preferredTransform))
    return size
}

struct VideoPlayer : UIViewControllerRepresentable {
    @Binding var player : AVPlayer
    let showControls: Bool
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<VideoPlayer>) -> AVPlayerViewController {

        let controller = AVPlayerViewController()
        controller.player = player
        controller.showsPlaybackControls = showControls
        controller.allowsVideoFrameAnalysis = false
        return controller
    }
    
    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<VideoPlayer>) { }
}
1

There are 1 best solutions below

1
workingdog support Ukraine On

Try this simple approach using a flag (isfullScreen) to present a full screen inplace.

Example code (with double tap):

struct ContentView: View {
    let example = "https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4"
//    let example = "https://drive.google.com/file/d/1fom7lDNjP7thxTuowYtsUXbErvX2jhzH/view?usp=sharing"
     
    @State var isfullScreen = false
    
    var body: some View {
        if isfullScreen {
            MainVideoPlayer(url: URL(string: example)!, isfullScreen: $isfullScreen)
        } else {
            ScrollView {
                //some views
                Text("view-1")
                
                MainVideoPlayer(url: URL(string: example)!, isfullScreen: $isfullScreen)
                
                Text("view-2")
                //some views
            }
        }
    }
}

struct MainVideoPlayer: View {
    let url: URL
    @Binding var isfullScreen: Bool
    @State var player = AVPlayer()
    
    var body: some View {
        GeometryReader { geo in
            AVKit.VideoPlayer(player: player) // <--- for my testing
                .frame(width: isfullScreen ? geo.size.width : 300, height: isfullScreen ? geo.size.height : 300)
                .onDisappear {
                    player.pause()
                }
                .onAppear {
                    player = AVPlayer(url: url)
                    player.play()
                }
                .overlay {
                    Color.white.opacity(0.0001)
                        .ignoresSafeArea()
                        .onTapGesture(count: 2){
                            isfullScreen.toggle()
                        }
                }
        }
    }
}