2015-02-23 72 views
14

我在UIViewControllerviewDidAppear方法中创建了AVPlayerViewController和附加的AVPlayer。但是,当我按下“完成”按钮时,我的自定义视图控制器会自动解除。如何拦截在AVPlayerViewController中完成按钮的敲击?

我想拦截这个动作,以便使用我自己的放松Segue,但我不知道如何做到这一点。我找到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]; 
    } 
} 

我要求苹果公司对这个问题,他们已经答复如下:

感谢您的苹果开发者技术支持联系(DTS )。我们的 工程师已审核您的请求,并得出结论认为 没有支持的方式来实现 当前的运输系统配置所需的功能。

回答

5

我的子类AVPlayerViewController,并发布来自viewWillDisappear的通知,指示解散AVPlayerViewController。

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

这可能不是100%正确的(因为如果你有被显示在AVPlayerViewController另一种观点认为它会失败),但它的工作对我来说AVPlayerViewController总是在堆栈的顶部。

+0

当用户按下完成按钮时,我想继续在小屏幕上播放视频,所以我需要avplayer对象,我该怎么办? – 2015-08-19 12:23:37

+1

当您按完成时,AVPlayerViewController将停止播放并释放AVPlayer。对于你所需要的,用AVPlayer创建你自己的ViewController,当用户按下完成时,调整视图的大小。 https://developer.apple.com/library/ios/samplecode/AVPlayerDemo/Introduction/Intro.html有一个创建自己的AVPlayerViewController的例子 – dev 2015-08-19 13:18:47

+1

如果我不想子类化AVPlayerViewController(如Apple所示:不要子类AVPlayerViewController覆盖此类的方法不受支持,并导致未定义的行为 此处:https://developer.apple.com/library/prerelease/ios/documentation/AVFoundation/Reference/AVPlayerViewController_Class/index.html)有没有办法获得通知? – 2015-08-26 09:06:46

2

你可以通过继承AVPLayerViewController来实现。但是由于这个原因有一些不确定的行为。 (在苹果文档中给出。见下文) 我也尝试了子类化AVPlayerViewController和我面临的内存问题。

根据苹果的文档:

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

当前,它们在AVPlayerViewController关闭时不提供回调。 见苹果开发者论坛:

在线程1苹果小伙子说:

我仍然相信,通过利用手动管理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

如果用户暂停视频该怎么办?这是否算作停止? – Jonny 2017-09-25 08:41:06

-1

一种方式是通过搜索相应的UIButton AVPlayerViewController的子视图里面附加的操作添加到在AVPlayerViewController现有的“完成”按钮。一旦按钮被发现,使用addTarget添加自定义操作:操作: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 
} 
4

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

我不觉得像AVPlayerViewController继承,因为它不是由苹果公司支持,可能会在未来的iOS更新的一个开拓一罐蠕虫。

我的解决办法是有一个定时器火灾每200毫秒,检查以下条件:

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

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

+0

这是最安全的解决方案。 – 2017-10-25 08:43:20

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,你也许可以为您在viewDidAppear/VC viewWillAppear中A内部每当那些被称为,至少我们知道,AVPlayerViewController不再显示,从而不应该玩。

1

我解决了保持对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() 
     } 
}