How do I intercept tapping of the done button in AVPlayerViewController?

前端 未结 10 686
渐次进展
渐次进展 2021-01-03 22:45

I created an AVPlayerViewController and an attached AVPlayer in the viewDidAppear method of a custom UIViewController. Ho

相关标签:
10条回答
  • 2021-01-03 23:17

    You could do it by subclassing AVPLayerViewController. But there is some undefined behaviour due to this. (Given in Apple docs. See below) I also tried subclassing AVPlayerViewController and I faced Memory issue.

    According to the Apple Docs:

    Do not subclass AVPlayerViewController. Overriding this class’s methods is unsupported and results in undefined behavior.

    Currently, they do not offer a callback when the AVPlayerViewController is dismissed. See Apple developer forum:

    • Thread1
    • Thread2

    In thread1 Apple guy says:

    I still believe that managing the exit of the AVPlayerViewController instance manually by utilizing the recommended gesture method above would be the most reliable way to know that the player view controller has been dismissed, since you'll be in charge of its dismissal at that point

    Hope it helps!


    Well, there is a fix to the problem. You can add a button as a subview of AVPlayerViewController. By doing so, you could intercept done button tap gesture.

    0 讨论(0)
  • 2021-01-03 23:25

    I subclassed AVPlayerViewController and am posting a notification from viewWillDisappear to indicate dismissing of AVPlayerViewController.

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

    This might not be 100% correct (as it would fail if you have another view being displayed over AVPlayerViewController), but it worked for me as AVPlayerViewController is always at the top of the stack.

    0 讨论(0)
  • 2021-01-03 23:25

    One way is to add additional action to the existing "Done" button on the AVPlayerViewController by searching the respective UIButton inside the subviews of AVPlayerViewController. Once the button is found, add a custom action using addTarget:action:forControlEvents:

    At least this works for me.

    - (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
    }
    
    0 讨论(0)
  • 2021-01-03 23:30

    Since there don't seem to be any perfect answers here, a workaround you can use for some situations is to monitor whether the AVPlayer is still playing and set an observer in case it closes automatically after being played all the way through.

      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 讨论(0)
提交回复
热议问题