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

问题描述 投票:17回答:8

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

我想拦截这个动作,以便使用我自己的放松塞格,但我不知道该怎么做。我找到了MPMoviePlayerViewController但不是AVPlayerViewController的例子。

我在MPMoviePlayerViewController找到的代码如下:

- (void)playVideo:(NSString *)aVideoUrl {
    // Initialize the movie player view controller with a video URL string
    MPMoviePlayerViewController *playerVC = [[[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL URLWithString:aVideoUrl]] autorelease];

    // Remove the movie player view controller from the "playback did finish" notification observers
    [[NSNotificationCenter defaultCenter] removeObserver:playerVC
                                                name:MPMoviePlayerPlaybackDidFinishNotification
                                              object:playerVC.moviePlayer];

    // Register this class as an observer instead
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(movieFinishedCallback:)
                                                 name:MPMoviePlayerPlaybackDidFinishNotification
                                               object:playerVC.moviePlayer];

    // Set the modal transition style of your choice
    playerVC.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;

    // Present the movie player view controller
    [self presentModalViewController:playerVC animated:YES];

    // Start playback
    [playerVC.moviePlayer prepareToPlay];
    [playerVC.moviePlayer play];
}

- (void)movieFinishedCallback:(NSNotification *)aNotification {
    // Obtain the reason why the movie playback finished
    NSNumber *finishReason = [[aNotification userInfo] objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];

    // Dismiss the view controller ONLY when the reason is not "playback ended"
    if ([finishReason intValue] != MPMovieFinishReasonPlaybackEnded) {
        MPMoviePlayerController *moviePlayer = [aNotification object];

        // Remove this class from the observers
        [[NSNotificationCenter defaultCenter] removeObserver:self
                                                        name:MPMoviePlayerPlaybackDidFinishNotification
                                                      object:moviePlayer];

        // Dismiss the view controller
        [self dismissModalViewControllerAnimated:YES];
    }
}

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

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

ios objective-c ios8 xcode6
8个回答
5
投票

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

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

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


5
投票

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

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

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

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

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


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
投票

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

根据Apple Docs:

不要子类化AVPlayerViewController。覆盖此类的方法不受支持,并导致未定义的行为。

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

在线程1 Apple的家伙说:

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

希望能帮助到你!


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


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
    //*******
  }

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

0
投票

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


-1
投票

一种方法是通过搜索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
}
© www.soinside.com 2019 - 2024. All rights reserved.