如何拦截在AVPlayerViewController中点击完成按钮?

时间:2015-02-23 10:26:44

标签: ios objective-c ios8 xcode6

我在自定义AVPlayerViewController的{​​{1}}方法中创建了AVPlayer和附加的viewDidAppear。但是,当我按下“完成”按钮时,我的自定义视图控制器会自动关闭。

我想拦截这个动作以便使用我自己的放松塞格,但我不知道该如何做到这一点。我找到了UIViewController但不是MPMoviePlayerViewController的示例。

我为AVPlayerViewController找到的代码如下:

MPMoviePlayerViewController

我向Apple询问了这个问题,他们回答如下:

  

感谢您与Apple开发者技术支持(DTS)联系。我们的   工程师已经审查了您的请求并得出结论   没有支持的方法来实现所需的功能   目前正在发货系统配置。

10 个答案:

答案 0 :(得分:5)

我将AVPlayerViewController子类化,并发布了一个来自viewWillDisappear的通知,表示解雇了AVPlayerViewController。

- (void) viewWillDisappear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] postNotificationName:kPlayerViewDismissedNotification object:nil];
    [super viewWillDisappear:animated];
}

这可能不是100%正确(因为如果你在AVPlayerViewController上显示另一个视图会失败),但它对我有效,因为AVPlayerViewController总是在堆栈顶部。

答案 1 :(得分:5)

Apple没有提供处理完成按钮的内置方式这一事实令人失望。

我不想从AVPlayerViewController继承,因为Apple不支持它,并且可能会在下一个iOS更新中打开一堆蠕虫。

我的解决方法是每200毫秒启动一次定时器并检查以下情况:

if (playerVC.player.rate == 0 &&
   (playerVC.isBeingDismissed || playerVC.nextResponder == nil)) {
  // Handle user Done button click and invalidate timer
}

播放器的rate属性为0表示视频不再播放。如果视图控制器被解雇或已被解雇,我们可以安全地假设用户单击了“完成”按钮。

答案 2 :(得分:4)

我通过保持对AVPlayerViewController实例的弱引用来解决,并使用定时器进行监视,其中引用更改为nil。

private weak var _playerViewController : AVPlayerViewController? // global reference
    ...
    ...
    let playerController = AVPlayerViewController() // local reference
    ...
    self.present(playerController, animated: true) { [weak self] in
        playerController.player?.play()
        self?._playerViewController = playerController
        // schedule a timer to intercept player dismissal
        Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] (timer) in
        if self?.playerViewController == nil {
            // player vc dismissed
            timer.invalidate()
        }
}

答案 3 :(得分:3)

你可以通过继承AVPLayerViewController来实现。但由于这个原因,存在一些未定义的行为。 (在Apple文档中给出。见下文) 我也试过继承AVPlayerViewController,我遇到了内存问题。

根据Apple文档:

  

不要继承AVPlayerViewController。覆盖这个类   方法不受支持,导致未定义的行为。

目前,当AVPlayerViewController被解除时,它们不提供回调。 请参阅Apple开发人员论坛:

在thread1中,Apple的家伙说:

  

我仍然相信管理AVPlayerViewController的退出   通过利用上面推荐的手势方法手动实例   将是知道玩家视图控制器的最可靠方式   已被解雇,因为你将负责解雇   那一点

希望它有所帮助!

嗯,有一个问题的解决方法。您可以添加一个按钮作为AVPlayerViewController的子视图。通过这样做,您可以拦截完成按钮轻击手势。

答案 4 :(得分:2)

我不确定这是否对您的用例有用,但是如果您可以针对iOS 12 +,AVPlayerViewController确实提供了委托属性/协议(AVPlayerViewControllerDelegate)以及一些可能有用的相关委托方法(请参阅下文) : 特别是在iOS 12+上:

    func playerViewController(_ playerViewController: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) 

在tvOS 11+上:

    func playerViewControllerShouldDismiss(_ playerViewController: AVPlayerViewController) -> Bool 
    func playerViewControllerWillBeginDismissalTransition(_ playerViewController: AVPlayerViewController) 
    func playerViewControllerDidEndDismissalTransition(_ playerViewController: AVPlayerViewController)

答案 5 :(得分:0)

由于此处似乎没有任何完美的答案,您可以在某些情况下使用的解决方法是监控AVPlayer是否仍在播放并设置观察者以防万一在播放后自动关闭通过

  var player:AVPlayer = AVPlayer()
  var videoPlayTimer:NSTimer = NSTimer()

  func playVideo(action: UIAlertAction)  -> Void {

    player = AVPlayer(URL: NSURL(fileURLWithPath: myFilePath))
    player.actionAtItemEnd = .None

    let playerController = AVPlayerViewController()
    playerController.player = player
    self.presentViewController(playerController, animated: true) {      
      self.player.play()
      self.monitorVideoPlayStatus()
      NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MyViewController.onVideoEnd(_:)), name: AVPlayerItemDidPlayToEndTimeNotification, object: self.player.currentItem)
    }
  }

  //setting a timer to monitor video play status in case it is closed by user
  func monitorVideoPlayStatus(){
    if ((player.rate != 0) && (player.error == nil)) {
      NSLog("player is playing")
      videoPlayTimer = NSTimer.after(0.5, monitorVideoPlayStatus)
    } else {
      NSLog("player is NOT playing")
      onVideoEnd()
    }
  }

  //will be called when video plays all the way through
  func onVideoEnd(note: NSNotification){
    NSLog("onVideoEnd")
    onVideoEnd()

    //canceling video play monitor
    videoPlayTimer.invalidate()
  }

  func onVideoEnd(){
    NSLog("finished playing video")
    NSNotificationCenter.defaultCenter().removeObserver(self, name: AVPlayerItemDidPlayToEndTimeNotification, object: nil)

    //*******
    //DO WHATEVER YOU WANT AFTER VIDEO HAS ENDED RIGHT HERE
    //*******
  }

答案 6 :(得分:0)

以下是我设法检测点击次数的代码。

在应用程序委托类中。但它会检测该视图中找到的每个按钮。你可以添加一些控件,检查类似的标题。

-(UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
    if ([window.rootViewController isKindOfClass:[AVPlayerViewController class]]) {
        return UIInterfaceOrientationMaskAll;
    }
    else if(window.rootViewController.presentedViewController != nil)
    {
        if ([window.rootViewController.presentedViewController isKindOfClass:[AVPlayerViewController class]] || [window.rootViewController.presentedViewController isKindOfClass:NSClassFromString(@"AVFullScreenViewController")]) {
            if ([window.rootViewController.presentedViewController isKindOfClass:NSClassFromString(@"AVFullScreenViewController")]) {
                [self findAllButton:window.rootViewController.presentedViewController.view];
            }
            return UIInterfaceOrientationMaskAll;
        }
    }
    [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"];
    [[UIApplication sharedApplication] setStatusBarHidden:NO];
    return UIInterfaceOrientationMaskPortrait;
}

-(void)findAllButton:(UIView*)view{
    for (UIView *subV in view.subviews) {
        if ([subV isKindOfClass:[UIButton class]]) {
            NSLog(@"%@",[((UIButton*)subV) titleForState:UIControlStateNormal]);
            [((UIButton*)subV) addTarget:self action:@selector(doneButtonCliked) forControlEvents:UIControlEventTouchUpInside];
        }
        else{
            [self findAllButton:subV];
        }
    }
}
-(IBAction)doneButtonCliked{
    NSLog(@"DONECLICK");
}

答案 7 :(得分:0)

如果您有一个显示AVPlayerViewController的视图控制器A,您可以检查VC A中的viewDidAppear / viewWillAppear。每当调用它们时,至少我们会知道AVPlayerViewController不再显示,因此不应该玩。

答案 8 :(得分:0)

不敢相信没有人会介绍最明显的解决方案:只需发出通知或执行AVPlayerViewController的{​​{1}}方法中需要执行的任何操作即可。 请不要持有对其的-dealloc引用,否则将不会调用strong

如果您不破坏内部逻辑(例如,不从覆盖的方法中调用-dealloc),则AVPlayerViewController的子类化没有问题(即使文档另有说明)。多年来(自iOS 8发布以来),我一直在使用以下代码,而没有任何运行时或AppStore提交问题:

super

因此,要回答该问题,只需将其添加到@interface VideoPlayerViewController : AVPlayerViewController @end @implementation VideoPlayerViewController - (UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskLandscape; } @end // now use VideoPlayerViewController instead of AVPlayerViewController 子类中:

AVPlayerViewController

如果您真的不敢继承类,请使用智能技术,将关联的对象附加到- (void)dealloc { // player was dismissed and is going to die, do any cleanup now } ,您可以控制其AVPlayerViewController,请参见:https://stackoverflow.com/a/19344475


要评论其他解决方案:

  • 计时器:仅用作最后的手段,请勿使计时器混乱代码
  • 使用属性观察来了解何时-dealloc的{​​{1}}变成AVPlayer并检查rate的{​​{1}}:聪明,但是在玩家暂停时失败事先(当然,可以使用计时器代替)
  • 使用0.0f的{​​{1}}:在播放器顶部显示某些内容时可能会失败,例如内置字幕选择器。一种类似的解决方案是在呈现视图控制器的AVPlayerViewController中检查玩家是否曾经出现过并且应该100%地工作。
  • 向“关闭”按钮添加另一个操作:我在iOS 8-13上成功使用了此解决方案很长时间,但是当我从Xcode 9切换到10后,该解决方案突然停止工作(例如,将用于从11编译为iOS的iOS SDK升级了) 12)

答案 9 :(得分:-1)

一种方法是向现有的" Done"添加额外的操作。通过搜索AVPlayerViewController子视图中的相应UIButton,AVPlayerViewController上的按钮。找到按钮后,使用addTarget:action:forControlEvents:

添加自定义操作

至少这对我有用。

- (UIButton*)findButtonOnView:(UIView*)view withText:(NSString*)text
{
    __block UIButton *retButton = nil;

    [view.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if([obj isKindOfClass:[UIButton class]]) {
            UIButton *button = (UIButton*)obj;
            if([button.titleLabel.text isEqualToString:text]) {
                retButton = button;
                *stop = YES;
            }
        }
        else if([obj isKindOfClass:[UIView class]]) {
            retButton = [self findButtonOnView:obj withText:text];

            if(retButton) {
                *stop = YES;
            }
        }
    }];

    return retButton;
}

- (void)showPlayer:(AVPlayer*)player
{
    AVPlayerViewController *vc = [[AVPlayerViewController alloc] init];
    vc.player = player;

    [self presentViewController:vc animated:NO completion:^{
        UIButton *doneButton = [self findButtonOnView:vc.view withText:@"Done"];
        [doneButton addTarget:self action:@selector(doneAction:) forControlEvents:UIControlEventTouchUpInside];
        [vc.player play];
    }];

}

- (void)doneAction:(UIButton*)button
{
    // perform any action required here
}