SwiftUI: How to properly present AVPlayerViewController modally?

swiftui modal
full screen modal swiftui
swiftui video
swiftui mp4
swiftui video player
swiftui music player
swift avplayer
swiftui record video
Proper UIKit Approach:

According to Apple's WWDC 2019 talk on the subject, AVPlayerViewController should be presented modally to take advantage of all the latest full-screen features of the API. This is the recommended sample code to be called from your presenting UIKit view controller:

// Create the player
let player = AVPlayer(url: videoURL)

// Create the player view controller and associate the player
let playerViewController = AVPlayerViewController()
playerViewController.player = player

// Present the player view controller modally
present(playerViewController, animated: true)

This works as expected and launches the video in beautiful full-screen.

Achieve the Same Effect with SwiftUI?:

In order to use the AVPlayerViewController from SwiftUI, I created the UIViewControllerRepresentable implementation:

struct AVPlayerView: UIViewControllerRepresentable {

    @Binding var videoURL: URL

    private var player: AVPlayer {
        return AVPlayer(url: videoURL)
    }

    func updateUIViewController(_ playerController: AVPlayerViewController, context: Context) {
        playerController.player = player
        playerController.player?.play()
    }

    func makeUIViewController(context: Context) -> AVPlayerViewController {
        return AVPlayerViewController()
    }
}

I cannot seem to figure out how to present this directly from SwiftUI in the same way as the AVPlayerViewController is presented directly from UIKit. My goal is simply to get all of the default, full-screen benefits.

So far, the following has not worked:

  • If I use a .sheet modifier and present it from within the sheet, then the player is embedded in a sheet and not presented full-screen.
  • I have also tried to create a custom, empty view controller in UIKit that simply presents my AVPlayerViewController modally from the viewDidAppear method. This gets the player to take on the full screen, but it also shows an empty view controller prior to display the video, which I do not want the user to see.

Any thoughts would be much appreciated!

Just a thought if you like to fullscreen similar like UIKit, did you try the following code from ContentView.

import SwiftUI
import UIKit
import AVKit

struct ContentView: View {
    let toPresent = UIHostingController(rootView: AnyView(EmptyView()))
    @State private var vURL = URL(string: "https://www.radiantmediaplayer.com/media/bbb-360p.mp4")
    var body: some View {
        AVPlayerView(videoURL: self.$vURL).transition(.move(edge: .bottom)).edgesIgnoringSafeArea(.all)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


struct AVPlayerView: UIViewControllerRepresentable {

    @Binding var videoURL: URL?

    private var player: AVPlayer {
        return AVPlayer(url: videoURL!)
    }

    func updateUIViewController(_ playerController: AVPlayerViewController, context: Context) {
        playerController.modalPresentationStyle = .fullScreen
        playerController.player = player
        playerController.player?.play()
    }

    func makeUIViewController(context: Context) -> AVPlayerViewController {
        return AVPlayerViewController()
    }
}

Hottest 'avplayerviewcontroller' Answers, SwiftUI: How to properly present AVPlayerViewController modally? Since it looks like the current behavior of AVPlayerViewController is to pause when exiting� To implement a proper modal presentation we need to make sure that the flag is changed properly. Maybe your modal screen is so custom that you want to change the flag from the modally presented view, but for the majority of cases it would be enough to change the flag when the screen is dismissed.

The solution explained by Razib-Mollick was a good start for me, but it was missing the use of the SwiftUI .sheet() method. I have added this by adding the following to ContentView:

    @State private var showVideoPlayer = false

    var body: some View {
        Button(action: { self.showVideoPlayer = true }) {
            Text("Start video")
        }
        .sheet(isPresented: $showVideoPlayer) {
            AVPlayerView(videoURL: self.$vURL)
                .edgesIgnoringSafeArea(.all)
        }
    }

But the problem is then, that the AVPlayer is instantiated again and again when SwiftUI re-renders the UI. Therefore the state of the AVPlayer has to move to a class object stored in the environment, so we can get hold of it from the View struct. So my latest solution looks now as follows. I hope it helps somebody else.

class PlayerState: ObservableObject {

    public var currentPlayer: AVPlayer?
    private var videoUrl : URL?

    public func player(for url: URL) -> AVPlayer {
        if let player = currentPlayer, url == videoUrl {
            return player
        }
        currentPlayer = AVPlayer(url: url)
        videoUrl = url
        return currentPlayer!
    }
}


struct ContentView: View {
    @EnvironmentObject var playerState : PlayerState
    @State private var vURL = URL(string: "https://www.radiantmediaplayer.com/media/bbb-360p.mp4")

    @State private var showVideoPlayer = false

    var body: some View {
        Button(action: { self.showVideoPlayer = true }) {
            Text("Start video")
        }
        .sheet(isPresented: $showVideoPlayer, onDismiss: { self.playerState.currentPlayer?.pause() }) {
            AVPlayerView(videoURL: self.$vURL)
                .edgesIgnoringSafeArea(.all)
                .environmentObject(self.playerState)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environmentObject(PlayerState())
    }
}


struct AVPlayerView: UIViewControllerRepresentable {

    @EnvironmentObject var playerState : PlayerState
    @Binding var videoURL: URL?

    func updateUIViewController(_ playerController: AVPlayerViewController, context: Context) {
    }

    func makeUIViewController(context: Context) -> AVPlayerViewController {
        let playerController = AVPlayerViewController()
        playerController.modalPresentationStyle = .fullScreen
        playerController.player = playerState.player(for: videoURL!)
        playerController.player?.play()
        return playerController
    }
}

Something to be aware of (a bug?): whenever a modal sheet is shown using .sheet() the environment objects are not automatically passed to the subviews. They have to be added using environmentObject(). Here is a link to read more about this problem: https://oleb.net/2020/sheet-environment/

How do you play a local movie file with AVPlayer, This will modally present the playerViewController for full screen playback, triggering the playback to begin as part of that view controller presentation� Tapping the close button while the AVPlayerController is in full screen works fine and doesn't cause the black screen. Interestingly, if I don't use any sort of container view or embedding, and just use a button to present an AVPlayerController modally, everything works fine and the gestures behave as they should.

Xcode 12 · iOS 14

→ Use .fullScreenCover instead of .sheet and you’re good to go.

See also: How to present a full screen modal view using fullScreenCover

How can I show an AVPlayerViewController in a container view and , Is there a proper way to embed a AVPlayerViewController in a container view and Or do I need to create & present a new AVPlayerViewController modally when For example when building my SwiftUI views, I often declare many variables� When presented modally, the AVPlayerViewController will helpfully show a 'Done' button which allows the user to dismiss the modal when they are finished watching. But there doesn't seem to be any way to detect when this dismissal happens.

AVPlayer & SwiftUI. SwiftUI is brand new in iOS 13 and with…, It's a great starter guide to help you get to grips with SwiftUI and how different it is to UIKit. What do we need to know first? Ok so we're all up to� Set Up and Configure the User Interface. After configuring your app’s audio session, you need to create the user interface for the player. Open the Main.storyboard file. In the Library’s search field, type button to find the Button object.

How to make a AVPlayerViewController go to fullscreen , Exit AVPlayerViewController fullscreen programmatically?, I have found a solution to SwiftUI: How to properly present AVPlayerViewController modally , Just a� In terms of output, create an AVPlayerViewController assign the player properly, and present the view controller. Also, take a look at the Mastering Modern Media Playback session from WWDC 2014 where we discuss how to use the above classes as well.

tvOS AVPlayerViewController Video Info, SwiftUI: How to properly present AVPlayerViewController modally , Just a thought if you like to fullscreen similar like UIKit, did you try the following code from� When every view controller is accounted for in a hierarchy, iOS can support these view controllers with appropriate transitions, layout and interactions. A view controller that’s presented modally, for example, can indicate that it wants to take up the whole screen.

Comments
  • You can overlay the AVPlayerView() on the current View
  • Do you want real full-screen covering everything or only covering a certain view, so excluding tab bars?
  • This worked for me when presented as a .sheet. The sheet will be displayed as a SwiftUI modal, with the small gap at the top. If you rotate the device or tap the expand to fullscreen button at the top-left, the player enters fullscreen mode.
  • What is the purpose of the toPresent = UIHostingController? Sorry if it's a dumb question, I'm just getting started in Swift development.