【问题标题】:How to make a AVPlayerViewController go to fullscreen programmatically?如何以编程方式使 AVPlayerViewController 进入全屏模式?
【发布时间】:2015-08-29 07:48:30
【问题描述】:

我正在尝试以编程方式使AVPlayerViewController 进入全屏模式,来自“嵌入式”模式,但是这似乎不适用于已发布的 API。

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

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

谢谢。

【问题讨论】:

  • 我的处境和你一样。似乎目前没有解决此问题的方法。 @Stefan 的回答实际上并没有“全屏”播放器,只是让它变大,正如您所说,这会导致控件无法正确更新,并且您无法知道该人是否单击了完成按钮或折叠按钮。但这并不重要,因为无论如何它都会以扩展箭头开头,这是不正确的。在我的情况下,我将不得不修改应用程序以解决 AVPlayerViewController 的限制。希望他们会在某个时候解决这个问题。
  • 同样的问题。这很奇怪,因为有一个apple tech talk,他们说您应该在其中展示视图控制器以使其全屏显示。

标签: ios objective-c swift ios8


【解决方案1】:

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

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

这会在左上角显示“完成”按钮。

【讨论】:

  • 无法实现这一点,当设备共享时(蓝色顶部栏)。即使在这种情况下,旧的 MPMoviePlayer 也可以全屏显示。
  • 这不会使用默认的过渡动画来全屏,对吧?相反,它使用模态。
【解决方案2】:

已针对 iOS 11 更新

不支持以编程方式使用 AVPlayerViewController 进入全屏模式(我认为这有点疏忽)。

但是,AVPlayerViewController does contain a private method 正是这样做的。您必须自己决定是否要使用它,因为您不应该调用私有方法。

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

【讨论】:

  • 这在 iOS 10 中仍然有效吗?我看到控制器响应了选择器,但调用时实际上没有任何反应。
  • 感谢@ToddH 它的工作!我们如何为这种全屏和小屏幕过渡添加观察者?
  • @VinuDavidJose 在 AVPlayerViewController 你必须 addObserver:forKeyPath:"videoBounds" 然后在被调用的 observeValue 方法中:DispatchQueue.main.async { isFullscreen = playerViewController.view.bounds != playerViewController.contentOverlayView?.bounds ... }
  • @ToddH 我已经使用这个想法从 tableviewcell 全屏显示。但它在显示全屏后在 ios 10 设备上崩溃。但在 ios 11 iPhone X 上完美运行
  • 请查找崩溃日志 *** 由于未捕获的异常 'UIViewControllerHierarchyInconsistency' 导致应用程序终止,原因:'子视图控制器: 应该有父视图控制器: 但是实际父级是:'
【解决方案3】:

在 iOS11 中,AVPlayerViewController 有 2 个新属性:entersFullScreenWhenPlaybackBegins 和 exitsFullScreenWhenPlaybackEnds。您可以在播放开始后立即启用全屏模式,并在播放结束时使用这些属性将其禁用。如果您需要在延迟一段时间后启用全屏模式,您可以使用 ToddH 中提到的 his answer 中提到的私有 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调用的解决方案不推荐使用。

【讨论】:

  • @karthikeyan 如果您需要为 AVPlayerViewController 强制全屏模式并且您可以使用私有 API 调用,只需将我的回答中的代码复制到您的项目,然后调用 myPlayerViewController。强制全屏模式()。还是您在这个答案中发现了一些错误?
  • @karthikeyan 或者这段代码不适合你?
  • 是的,它不起作用,我在第一次初始加载时显示播放器的一半视图,当用户单击全屏按钮时,它必须显示为横向模式。无论如何要找出输入完整或退出屏幕?我尝试了很多方法都没有帮助
  • @karthikeyan 您使用什么操作系统/设备进行测试?
  • 装有 iOS 11 的 iPhone 6s
【解决方案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】:

    作为对 ToddH 答案的一点 iOS 14 更新:要调用的 private API 是 enterFullScreenAnimated: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 中崩溃。我还没有研究如何通过闭包使其工作。

    【讨论】:

      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

        您只需设置 AVPlayerViewController 的 videoGravity 属性即可。

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

        【讨论】:

          【解决方案8】:

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

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

          【讨论】:

          • 我可以做 UIViewController.attemptRotationToDeviceOrientation() 而不是设置 playerVC.view 约束
          【解决方案9】:

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

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

          通常最好检查 Apple 示例代码以了解这些棘手的 API;我认为这个可能对很多 AVPlayer 用例有用:Using AVKit in iOS。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-02-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-06-07
            • 2011-04-13
            • 1970-01-01
            相关资源
            最近更新 更多