web-dev-qa-db-de.com

Wie kann ich das Tippen auf die Schaltfläche "Fertig" in AVPlayerViewController abfangen?

Ich habe eine AVPlayerViewController und eine angehängte AVPlayer in der viewDidAppear-Methode einer benutzerdefinierten UIViewController erstellt. Wenn ich jedoch die Schaltfläche "Fertig" drücke, wird mein benutzerdefinierter Ansichtscontroller automatisch geschlossen.

Ich möchte diese Aktion abfangen, um mein eigenes Abwicklungssegue zu verwenden, bin mir aber nicht sicher, wie ich das machen soll. Ich habe Beispiele für MPMoviePlayerViewController gefunden, aber nicht für AVPlayerViewController.

Der Code, den ich für MPMoviePlayerViewController gefunden habe, ist unten:

- (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];
    }
}

Ich habe Apple zu diesem Problem befragt und sie haben wie folgt geantwortet:

Vielen Dank, dass Sie sich an den technischen Support von Apple Developer (DTS) gewandt haben. Unsere Ingenieure haben Ihre Anfrage geprüft und sind zu dem Schluss gekommen, dass es angesichts der aktuellen Konfigurationen des Versandsystems keinen unterstützten Weg gibt, die gewünschte Funktionalität zu erreichen.

17
Simon

Die Tatsache, dass Apple keine integrierte Möglichkeit bietet, mit der Schaltfläche "Fertig" umzugehen, ist enttäuschend. 

Ich hatte keine Lust, von AVPlayerViewController zu erben, da es nicht von Apple unterstützt wird und wahrscheinlich in einem der nächsten iOS-Updates eine Dose Würmer öffnen wird.

Mein Workaround besteht darin, alle 200 ms einen Timer abzufeuern und die folgenden Bedingungen zu überprüfen:

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

Die rate-Eigenschaft des Players von 0 gibt an, dass das Video nicht mehr abgespielt wird. Und wenn der View-Controller bereits beendet wird, können wir davon ausgehen, dass der Benutzer auf die Schaltfläche Fertig geklickt hat.

5
Zyphrax

Ich habe AVPlayerViewController untergeordnet und gebe eine Benachrichtigung von viewWillDisappear an, um anzuzeigen, dass AVPlayerViewController nicht zugelassen wird.

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

Dies ist möglicherweise nicht zu 100% korrekt (da sonst ein Fehler auftreten würde, wenn eine andere Ansicht über AVPlayerViewController angezeigt wird), aber es funktionierte für mich, da AVPlayerViewController immer ganz oben im Stack ist.

5
dev

Ich habe das Problem gelöst, indem ich einen schwachen Verweis auf die AVPlayerViewController-Instanz behielt und mit einem Zeitgeber beobachtete, wo der Verweis auf null wechselt.

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()
        }
}
4
Marco Mardegan

Sie können dies tun, indem Sie AVPLayerViewController untergliedern. Daraus ergibt sich jedoch ein undefiniertes Verhalten. (In Apple-Dokumenten angegeben. Siehe unten.) Ich habe auch versucht, AVPlayerViewController zu klassifizieren, und ich hatte ein Problem mit dem Speicher.

Laut den Apple-Dokumenten:  

Unterklasse AVPlayerViewController nicht. Das Überschreiben der - Methoden dieser Klasse wird nicht unterstützt und führt zu undefiniertem Verhalten.

Derzeit bieten sie keinen Rückruf an, wenn der AVPlayerViewController geschlossen wird. Siehe Apple-Entwicklerforum: 

In thread1 sagt Apple-Typ: 

Ich bin immer noch der Meinung, dass die manuelle Verwaltung des Exits der Instanz AVPlayerViewController Mithilfe der empfohlenen Gestenmethode über Der zuverlässigste Weg ist, um zu wissen, dass der Player-View-Controller Verworfen wurde , da Sie zu an diesem Punkt für die Entlassung verantwortlich sind

Ich hoffe es hilft!


Nun, es gibt eine Lösung für das Problem. Sie können eine Schaltfläche als Unteransicht von AVPlayerViewController hinzufügen. Auf diese Weise können Sie die fertig gestellte Schaltfläche abtippen.

3
Utsav Dusad

Hier ist der Code, wie ich Klicks erkennen konnte.

In der Anwendung delegieren Klasse. Es erkennt jedoch alle gefundenen Schaltflächen in dieser Ansicht. Sie können etwas Kontrolle hinzufügen, Titel so etwas überprüfen.

-(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
mialkan

Da es hier scheinbar keine perfekten Antworten gibt, können Sie in einigen Situationen die Problembehebung verwenden, indem Sie überwachen, ob der AVPlayer noch läuft, und einen Beobachter einstellen, falls er automatisch geschlossen wird, nachdem er vollständig abgespielt wurde.

  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

Wenn Sie über einen View-Controller A verfügen, der eine AVPlayerViewController darstellt, könnten Sie wahrscheinlich viewDidAppear/viewWillAppear in VC A überprüfen. Wenn diese aufgerufen werden, wissen wir zumindest, dass der AVPlayerViewController nicht mehr angezeigt wird und daher nicht angezeigt wird nicht spielen.

0
Jonny