How to make a AVPlayerViewController go to fullscreen programmatically?

前端 未结 9 1093
既然无缘
既然无缘 2020-12-29 20:12

I\'m trying to make a AVPlayerViewController go to full screen mode programmatically, coming from \"embedded\" mode, however this does not appear to be possible with the pub

相关标签:
9条回答
  • 2020-12-29 20:44

    Updated for iOS 11

    There is no supported way to programmatically go fullscreen with AVPlayerViewController (a bit of an oversight in my opinion).

    However, AVPlayerViewController does contain a private method that does exactly that. You'll have to decide for yourself whether you'd want to use it or not given you're not supposed to call private methods.

    AVPlayerViewController+Fullscreen.h

    #import <AVKit/AVKit.h>
    
    @interface AVPlayerViewController (Fullscreen)
    
    -(void)goFullscreen;
    
    @end
    

    AVPlayerViewController+Fullscreen.m

    #import "AVPlayerViewController+Fullscreen.h"
    
    @implementation AVPlayerViewController (Fullscreen)
    
    -(void)goFullscreen {
        NSString *selectorForFullscreen = @"transitionToFullScreenViewControllerAnimated:completionHandler:";
        if (@available(iOS 11.3, *)) {
            selectorForFullscreen = @"transitionToFullScreenAnimated:interactive:completionHandler:";
        } else if (@available(iOS 11.0, *)) {
            selectorForFullscreen = @"transitionToFullScreenAnimated:completionHandler:";
        }
        SEL fsSelector = NSSelectorFromString([@"_" stringByAppendingString:selectorForFullscreen]);
        if ([self respondsToSelector:fsSelector]) {
            NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:fsSelector]];
            [inv setSelector:fsSelector];
            [inv setTarget:self];
    
            NSInteger index = 2; //arguments 0 and 1 are self and _cmd respectively, automatically set
            BOOL animated = YES;
            [inv setArgument:&(animated) atIndex:index];
            index++;
    
            if (@available(iOS 11.3, *)) {
                BOOL interactive = YES;
                [inv setArgument:&(interactive) atIndex:index]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation
                index++;
            }
    
            id completionBlock = nil;
            [inv setArgument:&(completionBlock) atIndex:index];
            [inv invoke];
        }
    }
    
    @end
    
    0 讨论(0)
  • 2020-12-29 20:49

    AVPlayerViewController is a subclass of UIViewController, so it is presentable like any other view controller subclass. Are you able to use presentViewController:animated:completion?

    self.avPlayerController.modalPresentationStyle = UIModalPresentationOverFullScreen;
    [self presentViewController:self.avPlayerController animated:YES completion:nil];
    

    This then shows the "Done" button in the top left-hand corner.

    0 讨论(0)
  • 2020-12-29 20:50

    For an 'embedded' AVPlayerViewController instance, it is quite easy to programmatically have it start playback in full screen mode, and without hacking anything (calling private methods). You just need to set its entersFullScreenWhenPlaybackBegins property to true.

    You need to add the controller as a child VC to the main VC, and that's basically it. In viewDidAppear(_:) you need to call play() method on the controller's player property - playback will be automatically started in fullscreen.

    It's often best to check Apple sample code for these kind of tricky APIs; I think this one might be useful for a lot of AVPlayer use cases: Using AVKit in iOS.

    0 讨论(0)
  • 2020-12-29 20:53

    In iOS11 there are 2 new properties for AVPlayerViewController: entersFullScreenWhenPlaybackBegins and exitsFullScreenWhenPlaybackEnds. You can enable full screen mode right after playback begins and disable it when playback ends with these properties. If you need to enable fullscreen mode after some delay you can use private API methods as ToddH mentioned in his answer. However in iOS11 _transitionToFullScreenViewControllerAnimated:completionHandler: method is not available anymore, there is the same method called _transitionToFullScreenAnimated:completionHandler:. The second method accepts the same arguments as the first one.

    I can show an example how to use it. First of all you need to create AVPlayerViewController instance in your UIViewController:

    private let playerController : AVPlayerViewController = {
    
        if let urlForPlayer = URL(string: "your_video_url") {
    
            $0.player = AVPlayer(url: urlForPlayer)
        }
        return $0
    } (AVPlayerViewController())
    

    Then you need to setup view for AVPlayerViewController and add it to your current controller view. Function setupAVplayerController can do it for you:

    private func setupAVplayerController() {
    
        self.addChildViewController(self.playerController)
        self.playerController.view.frame = CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200.0)
        self.view.addSubview(self.playerController.view)
        self.playerController.didMove(toParentViewController: self)
    }
    

    Function enterFullscreen forces full screen mode for AVPlayerViewController:

    private func enterFullscreen(playerViewController:AVPlayerViewController) {
    
        let selectorName : String = {
    
            if #available(iOS 11, *) {
    
                return "_transitionToFullScreenAnimated:completionHandler:"
            } else {
    
                return "_transitionToFullScreenViewControllerAnimated:completionHandler:"
            }
        }()
        let selectorToForceFullScreenMode = NSSelectorFromString(selectorName)
        if playerViewController.responds(to: selectorToForceFullScreenMode) {
    
                playerViewController.perform(selectorToForceFullScreenMode, with: true, with: nil)
        }
    }
    

    And now you need to call all these functions where you need it, for example in viewDidAppear:

    override func viewDidAppear(_ animated: Bool) {
    
        super.viewDidAppear(animated)
    
        //Your code
    
        self.setupAVplayerController()
        self.playerController.player?.play()
        DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
    
            self.enterFullscreen(playerViewController:self.playerController)
        }
    }
    

    Don't forget that this solution based on private API calls that is not recommended to use.

    0 讨论(0)
  • 2020-12-29 20:54

    As a little iOS 14 update to ToddH's answer: the private API to call is enterFullScreenAnimated:completionHandler:. So here's an extension on AVPlayerViewController to enter full screen.

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

    This implementation doesn't offer a completion callback. If you pass a Swift closure to the completionHandler parameter, it crashes in the underlying Obj-C API. I haven't investigated how to pass the closure to make it work.

    0 讨论(0)
  • 2020-12-29 21:01

    You can just set the videoGravity property of AVPlayerViewController.

    if(fullscreen)
    {
        [self.avPlayerController 
         setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    }
    else
    {
        [self.avPlayerController 
        setVideoGravity:AVLayerVideoGravityResizeAspect];
    }
    
    0 讨论(0)
提交回复
热议问题