faserx
faserx

Reputation: 317

AVPlayer exitFullScreen

I've a problem with SwiftUI and AVPlayer. When i rotate the device in landscape mode, the player go in fullscreen mode, but when I rotate in portrait it not exit.
The struct for AVPlayer:

import SwiftUI
import AVKit

struct AVPlayerControllerRepresentable: UIViewControllerRepresentable {
    @Binding var showFullScreen: Bool
    @Binding var player: AVPlayer
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<AVPlayerControllerRepresentable>) -> AVPlayerViewController {
        print("makeUIViewController->",showFullScreen)
        let controller  = AVPlayerViewController()
        controller.player = player
        controller.showsPlaybackControls = false;
        chooseScreenType(controller)
        return controller
    }
    
    func updateUIViewController(_  uiViewController: AVPlayerViewController , context: UIViewControllerRepresentableContext<AVPlayerControllerRepresentable>) {
        print("updateUIViewController->",showFullScreen)
        chooseScreenType(uiViewController)
    }
    
    private func chooseScreenType(_ controller: AVPlayerViewController) {
        print("chooseScreenType", self.showFullScreen)
        self.showFullScreen ? controller.enterFullScreen(animated: true) : controller.exitFullScreen(animated: true)
    }
    
}

extension AVPlayerViewController {
    func enterFullScreen(animated: Bool) {
        print("Enter full screen")
        perform(NSSelectorFromString("enterFullScreenAnimated:completionHandler:"), with: animated, with: nil)
    }
    
    func exitFullScreen(animated: Bool) {
        print("Exit full screen")
        perform(NSSelectorFromString("exitFullScreenAnimated:completionHandler:"), with: animated, with: nil)
    }
}

And this is my View:

 VStack{
                       

         AVPlayerControllerRepresentable(showFullScreen: $fullScreen, player: $player)
                                    .ignoresSafeArea()
                                    .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
                                        DispatchQueue.main.async {
                                            print("change rotation->",UIDevice.current.orientation.rawValue)
                                            if UIDevice.current.orientation.isLandscape {
                                                print("landscape")
                                                self.fullScreen = true
                                            } else {
                                                print("portrait")
                                                self.fullScreen = false
                                            }
                                        }
                                    }
                                    .frame(width: 290, height: 220)
                                    .overlay {
                                       BoxTv()
                                    }
                                    .opacity(1.0)
                                    .padding([.bottom, .top], 40)
                            }.onAppear(){
                                self.player.play();
    }

Can anyone help me? When rotate device in portrait mode, the function 'exitFullScreen' not called

Upvotes: 2

Views: 946

Answers (1)

Asperi
Asperi

Reputation: 257493

SwiftUI seems get lost connection with representable in this scenario... anyway it is better UIKit things to be handled within UIKit flow. Representable concept has Coordinator for such cases.

So a possible approach to fix is to move everything inside AVPlayerControllerRepresentable.

enter image description here

Here is main part (tested with Xcode 13.4 / iOS 15.5):

    func makeUIViewController(context: UIViewControllerRepresentableContext<AVPlayerControllerRepresentable>) -> AVPlayerViewController {
        let controller  = AVPlayerViewController()
        controller.player = player
        controller.showsPlaybackControls = false;

        context.coordinator.playerController = controller
        return controller
    }

    class Coordinator: NSObject, AVPlayerViewControllerDelegate {
        weak var playerController: AVPlayerViewController? {
            didSet {
                playerController?.delegate = self
            }
        }

        private var subscriber: AnyCancellable? = nil
        override init() {
            super.init()
            subscriber = NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)
                .sink { [weak self] _ in
                    self?.rotated()
                }
        }

        func rotated() {
            if UIDevice.current.orientation.isLandscape {
                self.enterFullScreen(animated: true)
            } else {
                self.exitFullScreen(animated: true)
            }
        }

    //...

Test module on GitHub

Upvotes: 1

Related Questions