PIYO - Tech & Life -

MPMoviePlayerViewControllerが再生完了時に勝手に閉じて困る

Objective-C iOS

iOSには動画再生用にMPMovierPlayerViewControllerっていうクラスがあって、URLを渡して表示するだけでめちゃくちゃ簡単に動画再生機能を実装できる。すごく便利。

その分自由度が低くてちょっとした挙動を変えるのが大変だったりする。

まず、普通に動画を再生するならこういう風に実装する。

- (void)playVideo:(NSURL*)url
    MPMoviePlayerViewController *vc = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
    [self presentViewController:vc animated:YES completion:nil];
}

これだけのコードで再生できるから結構驚き。

ところで、この方法では再生完了時にMPMoviePlayerViewControllerが勝手に閉じられてしまう。再生が終わったらそのまま待つのがいいんじゃないの、と個人的には思ったため回避方法を探すことにした。

どうやら内部ではNSNotificationCenterを用いて動画終了時に通知を出し、その通知を受け取ったら閉じるというセレクタを呼んでいるらしい。

Dismissing MPMoviePlayerViewController the right way | I’m the one who codes!

勝手に閉じないようにするにはプレーヤーの表示前にNSNotificationCenterから関連するものを削除してあげればよい。

- (void)playVideo:(NSURL*)url
    MPMoviePlayerViewController *vc = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
    [[NSNotificationCenter defaultCenter] removeObserver:vc
        name:MPMoviePlayerPlaybackDidFinishNotification
        object:vc.moviePlayer];
    [self presentViewController:vc animated:YES completion:nil];
}

これでOKと思いきやそうもいかない。なんとMPMoviePlayerViewControllerがデフォルトで用意しているcloseボタンまで効かなくなってしまう。もちろんこんな挙動は困る。

回避するには、さっき削除したMPMoviePlayerPlaybackDidFinishNotificationに対応するセレクタを自前で定義し、ユーザーがボタンを操作したときには閉じるという処理にしておけばいい。

最終的なコードは次のようになる。

- (void)playVideo:(NSURL*)url
    MPMoviePlayerViewController *vc = [[MPMoviePlayerViewController alloc] initWithContentURL:url];

    [[NSNotificationCenter defaultCenter] removeObserver:vc
        name:MPMoviePlayerPlaybackDidFinishNotification
        object:vc.moviePlayer];

    [[NSNotificationCenter defaultCenter] addObserver:self 
        selector:@selector(videoFinished:)
        name:MPMoviePlayerPlaybackDidFinishNotification 
        object:vc.moviePlayer];

    [self presentViewController:vc animated:YES completion:nil];
}

- (void)videoFinished:(NSNotification*)notification{
    int value = [[notification.userInfo valueForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
    if (value == MPMovieFinishReasonUserExited) {
        [self dismissMoviePlayerViewControllerAnimated];
    }
}