2015-02-23 6 views
14

Ich habe eine AVPlayerViewController und eine beigefügte in der viewDidAppear Methode einer benutzerdefinierten UIViewController erstellt. Wenn ich jedoch die Schaltfläche "Fertig" drücke, wird mein benutzerdefinierter Ansichts-Controller automatisch geschlossen.Wie kann ich das Antippen der Schaltfläche "Fertig" in AVPlayerViewController abfangen?

Ich möchte diese Aktion abfangen, um mein eigenes Abwickeln Segue zu verwenden, aber ich bin nicht sicher, wie man das macht. Ich habe Beispiele für MPMoviePlayerViewController aber nicht AVPlayerViewController gefunden.

Der Code, den ich für MPMoviePlayerViewController gefunden unter:

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

fragte ich von Apple über dieses Problem und sie haben wie folgt geantwortet:

Technischen Support Vielen Dank für Ihre Kontaktaufnahme Apple Developer (DTS). Unsere Ingenieure haben Ihre Anfrage überprüft und sind zu dem Schluss gekommen, dass es keinen unterstützten Weg gibt, um die gewünschte Funktionalität zu erreichen, die derzeit Versandsystemkonfigurationen gegeben.

Antwort

5

Ich habe AVPlayerViewController subclassiert und gebe eine Benachrichtigung von viewWillDisappear aus, um die Ablehnung von AVPlayerViewController anzuzeigen.

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

Dies könnte nicht 100% richtig sein (wie es, wenn Sie über AVPlayerViewController eine andere Ansicht angezeigt haben würde fehlschlagen wird), aber es funktionierte für mich als AVPlayerViewController immer an der Spitze des Stapels ist.

+0

Wenn der Benutzer die Done-Taste drückt, möchte ich weiterhin auf einem kleinen Bildschirm Video abspielen, also brauche ich ein AVplayer-Objekt, was soll ich tun? –

+1

AVPlayerViewController stoppt die Wiedergabe und gibt den AVPlayer frei, wenn Sie done drücken. Für was Sie brauchen, erstellen Sie Ihren eigenen ViewController mit AVPlayer und wenn der Benutzer done drückt, ändern Sie einfach die Größe der Ansicht. https://developer.apple.com/library/ios/samplecode/AVPlayerDemo/Introduction/Intro.html hat ein Beispiel zum Erstellen eines eigenen AVPlayerViewControllers – dev

+1

Falls ich den AVPlayerViewController nicht ableiten möchte (wie Apple vorschlägt: Do not Unterklasse AVPlayerViewController.Das Überschreiben der Methoden dieser Klasse wird nicht unterstützt und führt zu undefiniertem Verhalten. hier: https://developer.apple.com/library/prerelease/ios/documentation/AVFoundation/Reference/AVPlayerViewController_Class/index.html) gibt es einen Weg um eine Benachrichtigung zu erhalten? –

2

Sie könnten dies tun, indem Sie AVPLayerViewController ableiten. Aber dadurch gibt es ein undefiniertes Verhalten. (Gegeben in Apple Docs. Siehe unten) Ich habe auch versucht, AVPlayerViewController Subclassing und ich konfrontiert Memory-Problem.

Nach dem Apple-Docs:

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

Gegenwärtig wird kein Rückruf angeboten, wenn der AVPlayerViewController beendet wird. Siehe Apple-Entwickler-Forum:

In Thread1 Apple-Typ sagt:

Ich glaube immer noch, dass die Ausfahrt der Instanz AVPlayerViewController Verwaltung manuell durch den Einsatz die empfohlene Gestenmethode über wäre der zuverlässigste Weg t o wissen, dass der Spieler-View-Controller wurde abgewiesen, da man bei verantwortlich für seine Entlassung sein werde, dass Punkt

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 Schaltfläche zum Fertigstellen der Schaltflächen abfangen.

0

Da es hier keine perfekten Antworten zu geben scheint, können Sie in einigen Situationen versuchen, zu überwachen, ob der AVPlayer noch spielt, und einen Beobachter einstellen, falls er nach dem Durchspielen automatisch geschlossen wird .

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

Was passiert, wenn der Benutzer das Video pausiert? Ist das ein Stopp? – Jonny

-1

Eine Möglichkeit ist, auf dem AVPlayerViewController zusätzliche Maßnahmen zur bestehenden Schaltfläche „Fertig“ hinzufügen, indem Sie die entsprechende UIButton innerhalb der Subviews von AVPlayerViewController suchen. Sobald die Schaltfläche gefunden ist, fügen Sie eine benutzerdefinierte Aktion mit addTarget hinzu: action: forControlEvents:

Zumindest funktioniert das für mich.

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

Die Tatsache, dass Apple keine integrierte Möglichkeit bietet, die Schaltfläche "Fertig" zu verarbeiten, ist enttäuschend.

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

Meine Abhilfe ist ein Timer Feuer alle 200 ms haben und für die folgende Bedingung überprüfen:

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

Der rate Eigenschaft des Spielers von 0 zeigt an, dass das Video nicht mehr zu spielen. Und wenn der View-Controller abgewiesen oder bereits geschlossen wird, können wir davon ausgehen, dass der Benutzer auf die Schaltfläche Fertig geklickt hat.

+0

Dies ist die sicherste Lösung imo. –

0

Hier ist Code wie ich Klicks festgestellt habe.

In Anwendung Delegate-Klasse. Aber es erkennt jeden Knopf 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

Wenn Sie einen View-Controller-A haben, die eine AVPlayerViewController präsentiert, könnten Sie wahrscheinlich prüfen in viewDidAppear/viewWillAppear innerhalb VC A. Jedes Mal, wenn diejenigen genannt werden, zumindest würden wir wissen, dass die AVPlayerViewController nicht mehr angezeigt wird und somit sollte nicht spielen.

1

Ich löste, indem ich einen schwachen Verweis auf die AVPlayerViewController-Instanz behielt und mit einem Zeitgeber überwachte, bei dem die Referenz auf Null geändert wurde.

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