Как перехватить нажатие сделанной кнопки в AVPlayerViewController?

Я создал AVPlayerViewController и прикрепленный AVPlayer в методе viewDidAppear пользовательского UIViewController. Однако, когда я нажимаю кнопку "Готово", мой пользовательский контроллер просмотра автоматически отбрасывается.

Я хотел бы перехватить это действие, чтобы использовать мой собственный сеанс 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];
    }
}

Я спросил у Apple об этой проблеме, и они ответили следующим образом:

Благодарим вас за обращение в службу технической поддержки Apple Developer (DTS). наш инженеры рассмотрели ваш запрос и пришли к выводу, что есть нет поддерживаемого способа достижения желаемой функциональности, учитывая в настоящее время отправляет конфигурации системы.

Ответ 1

Я подклассифицировал AVPlayerViewController и отправляю уведомление из viewWillDisappear, чтобы указать отклонение AVPlayerViewController.

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

Это может быть не на 100% правильно (поскольку это может быть неудачно, если у вас есть другой вид, отображаемый над AVPlayerViewController), но он работал у меня, поскольку AVPlayerViewController всегда находится в верхней части стека.

Ответ 2

Тот факт, что Apple не поддерживает встроенный способ обработки кнопки "Готово", разочаровывает.

Мне не хотелось наследовать от AVPlayerViewController, потому что он не поддерживается Apple и, вероятно, откроет банку червей в одном из следующих обновлений iOS.

Мое обходное решение состоит в том, чтобы стрелять таймером каждые 200 мс и проверять следующее условие:

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

Свойство player rate 0 указывает, что видео больше не воспроизводится. И если диспетчер представлений отклоняется или уже уволен, мы можем с уверенностью предположить, что пользователь нажал кнопку "Готово".

Ответ 3

Вы можете сделать это путем подкласса AVPLayerViewController. Из-за этого существует некоторое поведение undefined. (Представлено в документах Apple. См. Ниже) Я также попытался подклассифицировать AVPlayerViewController, и я столкнулся с проблемой памяти.

Согласно Apple Docs:

Не подклассы AVPlayerViewController. Переопределение этих классов методы не поддерживаются и приводят к поведению undefined.

В настоящее время они не предлагают обратный вызов, когда AVPlayerViewController отклоняется. См. Форум разработчиков Apple:

В thread1 Apple говорит:

Я все еще считаю, что управление выходом из AVPlayerViewController вручную, используя рекомендуемый метод жестов выше был бы самым надежным способом узнать, что контроллер просмотра игрока был уволен, поскольку вы будете отвечать за его увольнение по эта точка

Надеюсь, что это поможет!


Ну, есть проблема с этой проблемой. Вы можете добавить кнопку в качестве подзапроса AVPlayerViewController. Поступая таким образом, вы можете перехватить жест нажатой кнопки.

Ответ 4

Поскольку здесь нет идеальных ответов, обходной путь, который вы можете использовать для некоторых ситуаций, - это отслеживать, продолжает ли 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
    //*******
  }

Ответ 5

Вот код, как мне удалось обнаружить клики.

В классе делегирования приложения. Но он обнаруживает, что каждая кнопка находит в этом виде. Вы можете добавить некоторый элемент управления, проверить заголовок что-то вроде этого.

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

Ответ 6

Если у вас есть контроллер вида A, который представляет AVPlayerViewController, вы, вероятно, можете проверить в viewDidAppear/viewWillAppear внутри VC A. Когда бы они ни назывались, по крайней мере, мы знали бы, что AVPlayerViewController больше не отображается и, следовательно, не должен играть.

Ответ 7

Один из способов - добавить дополнительные действия к существующей кнопке "Готово" на AVPlayerViewController, выполнив поиск соответствующего UIButton внутри подпрограмм 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
}