繁体   English   中英

如何以编程方式将 AVPlayerViewController go 全屏显示?

[英]How to make a AVPlayerViewController go to fullscreen programmatically?

我正在尝试以编程方式将AVPlayerViewController go 从“嵌入式”模式变为全屏模式,但是对于已发布的 API 这似乎是不可能的。

有没有我缺少的解决方法? 我有兴趣获得与用户按下控件右下角的全屏按钮时获得的相同的 animation。

使用 MPMoviePlayerController 不是一个可行的选择,因为我可能一次播放多个视频。

谢谢。

AVPlayerViewController 是 UIViewController 的子类,因此它可以像任何其他视图控制器子类一样呈现。 你能使用presentViewController:animated:completion吗?

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

然后在左上角显示“完成”按钮。

针对 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

在 iOS11 中, AVPlayerViewController有 2 个新属性: entersFullScreenWhenPlaybackBeginsexitsFullScreenWhenPlaybackEnds 您可以在播放开始后立即启用全屏模式,并在播放结束时禁用这些属性。 如果您需要在延迟一段时间后启用全屏模式,您可以使用ToddH他的回答中提到的私有 API 方法。 但是在 iOS11 中_transitionToFullScreenViewControllerAnimated:completionHandler:方法不再可用,有相同的方法称为_transitionToFullScreenAnimated:completionHandler: 第二种方法接受与第一种方法相同的参数。

我可以展示一个如何使用它的例子。 首先,您需要在UIViewController创建AVPlayerViewController实例:

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 调用的解决方案是不推荐使用的。

更新:ToddH 答案的 Swift 4 版本:

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

作为 ToddH 答案的 iOS 14 小更新:要调用的私有 APIenterFullScreenAnimated:completionHandler: 所以这里有一个关于 AVPlayerViewController 的扩展进入全屏。

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

此实现不提供完成回调。 如果您将 Swift 闭包传递给completionHandler参数,它会在底层 Obj-C API 中崩溃。 我还没有研究如何通过闭包使其工作。

ToddH 的 Swift 3 版本:

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

你可以只设置 AVPlayerViewController 的videoGravity属性。

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

我不需要使用任何受限代码。

为此,我假设您已将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())
        })
    }

对于“嵌入式” AVPlayerViewController实例,很容易以编程方式让它在全屏模式下开始播放,而无需破解任何内容(调用私有方法)。 您只需将其entersFullScreenWhenPlaybackBegins属性设置为 true。

您需要将控制器作为子 VC 添加到主 VC,基本上就是这样。 viewDidAppear(_:)您需要在控制器的player属性上调用play()方法 - 播放将自动以全屏方式开始。

对于这些棘手的 API,通常最好检查 Apple 示例代码; 我认为这可能对很多 AVPlayer 用例有用: 在 iOS 中使用 AVKit

非常简单,只需设置playerViewController.videoGravity =.resizeAspectFill

它全屏显示:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM