Как сделать AVPlayerViewController доступным в полноэкранном режиме?

Я пытаюсь сделать AVPlayerViewController программным путем перейти в полноэкранный режим, исходя из "встроенного" режима, однако это не представляется возможным с опубликованным API.

Есть ли обходной путь, который мне не хватает? Я заинтересован в том, чтобы получить ту же анимацию, которую вы получаете, когда пользователь нажимает кнопку полного экрана в правом нижнем углу элементов управления.

Использование MPMoviePlayerController не является жизнеспособной альтернативой, поскольку у меня может быть одновременно несколько видеороликов.

Благодарю.

Ответ 1

AVPlayerViewController является подклассом UIViewController, поэтому он представлен как любой другой подкласс диспетчера представлений. presentViewController:animated:completion ли вы использовать presentViewController:animated:completion?

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

Затем отображается кнопка "Готово" в верхнем левом углу.

Ответ 2

Обновлено для iOS 11

Нет никакого поддерживаемого способа программного просмотра в полноэкранном режиме с помощью AVPlayerViewController (немного надзора, на мой взгляд).

Однако AVPlayerViewController содержит закрытый метод, который делает именно это. Вы сами должны решить, хотите ли вы использовать его или нет, если вы не должны называть частные методы.

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

Ответ 3

В iOS11 есть два новых свойства для AVPlayerViewController: entersFullScreenWhenPlaybackBegins и exitsFullScreenWhenPlaybackEnds. Вы можете включить полноэкранный режим сразу после начала воспроизведения и отключить его, когда воспроизведение заканчивается этими свойствами. Если вам нужно включить полноэкранный режим после некоторой задержки, вы можете использовать частные методы API, о которых упоминал Тоддх в своем ответе. Однако в iOS11 _transitionToFullScreenViewControllerAnimated:completionHandler: метод больше не доступен, существует тот же метод, что и _transitionToFullScreenAnimated:completionHandler: Второй метод принимает те же аргументы, что и первый.

Я могу показать пример, как его использовать. Прежде всего вам нужно создать экземпляр AVPlayerViewController в вашем UIViewController:

private let playerController : AVPlayerViewController = {

    if let urlForPlayer = URL(string: "your_video_url") {

        $0.player = AVPlayer(url: urlForPlayer)
    }
    return $0
} (AVPlayerViewController())

Затем вам нужно настроить представление для AVPlayerViewController и добавить его в текущий вид контроллера. Функция setupAVplayerController может сделать это за вас:

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)
}

Функция enterFullscreen полноэкранный режим для 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)
    }
}

И теперь вам нужно вызвать все эти функции там, где вам это нужно, например, в 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)
    }
}

Не забывайте, что это решение основано на частных вызовах API, которые не рекомендуется использовать.

Ответ 4

ОБНОВЛЕНИЕ: Swift 4 версия ответа ToddH:

private func enterFullscreen(playerViewController: AVPlayerViewController) {

    let selectorName: String = {
        if #available(iOS 11.3, *) {
            return "_transitionToFullScreenAnimated:interactive:completionHandler:"
        } else 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)
    }
}

Ответ 5

Версия Swift 3 для ответа ToddH:

extension AVPlayerViewController {

    func goFullScreen() {
        let selector = NSSelectorFromString("_transitionToFullScreenViewControllerAnimated:completionHandler:")
        if self.responds(to: selector) {
            // first argument is animated (true for me), second is completion handler (nil in my case)
            self.perform(selector, with: true, with: nil)
        }
    }
}

Ответ 6

Вы можете просто установить свойство videoGravity для AVPlayerViewController.

if(fullscreen)
{
    [self.avPlayerController 
     setVideoGravity:AVLayerVideoGravityResizeAspectFill];
}
else
{
    [self.avPlayerController 
    setVideoGravity:AVLayerVideoGravityResizeAspect];
}

Ответ 7

У меня не было необходимости использовать какой-либо ограниченный код.

Для этого я предполагаю, что вы добавили AVPlayerViewController в качестве дочернего контроллера представления.

Затем для этого сначала нужно удалить контроллер дочернего представления, а затем снова представить его как полноэкранный контроллер, а также правильно подключить представление AVPlayer к его родительскому представлению.

Вот как я это сделал. Обратите внимание, что я использую библиотеку под названием Easy Peasy для восстановления ограничений playerVC.view - это можно сделать и с соответствующими ограничениями.

    @objc func fullscreenButtonClicked() {

        playerVC.willMove(toParentViewController: nil)
        playerVC.view.removeFromSuperview()
        playerVC.removeFromParentViewController()


        self.present(self.playerVC, animated: false, completion: {
            self.playerVC.view.easy.layout(Top(), Right(), Left(), Bottom())
        })
    }