我在自定义AVPlayerViewController
的AVPlayer
方法中创建了viewDidAppear
和附加的UIViewController
。但是,当我按下“完成”按钮时,我的自定义视图控制器会自动关闭。
我想拦截这个动作,以便使用我自己的放松塞格,但我不知道该怎么做。我找到了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开发人员技术支持(DTS)联系。我们的工程师已经审核了您的请求,并得出结论,鉴于当前出货的系统配置,没有支持的方法来实现所需的功能。
我将AVPlayerViewController子类化,并发布了来自viewWillDisappear的通知,以指示解雇AVPlayerViewController。
- (void) viewWillDisappear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] postNotificationName:kPlayerViewDismissedNotification object:nil];
[super viewWillDisappear:animated];
}
这可能不是100%正确(因为如果你有另一个视图显示在AVPlayerViewController上会失败),但它对我有用,因为AVPlayerViewController总是在堆栈的顶部。
Apple没有提供内置方式来处理Done按钮这一事实令人失望。
我不想从AVPlayerViewController继承,因为Apple不支持它,并且可能会在下一个iOS更新中打开一堆蠕虫。
我的解决方法是每200毫秒启动一次计时器并检查以下情况:
if (playerVC.player.rate == 0 &&
(playerVC.isBeingDismissed || playerVC.nextResponder == nil)) {
// Handle user Done button click and invalidate timer
}
播放器的rate
属性为0表示视频不再播放。如果视图控制器被解雇或已被解雇,我们可以安全地假设用户单击了完成按钮。
我通过保持对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()
}
}
你可以通过继承AVPLayerViewController来做到这一点。但由于这个原因,存在一些未定义的行为。 (在Apple文档中给出。见下文)我也尝试了子类化AVPlayerViewController,我遇到了内存问题。
根据Apple Docs:
不要子类化AVPlayerViewController。覆盖此类的方法不受支持,并导致未定义的行为。
目前,当AVPlayerViewController被解除时,它们不提供回调。查看Apple开发人员论坛:
在线程1 Apple的家伙说:
我仍然相信通过利用上面推荐的手势方法手动管理AVPlayerViewController实例的退出将是最可靠的方式来知道玩家视图控制器已被解雇,因为你将在那时负责解雇
希望能帮助到你!
好吧,有一个问题的解决方法。您可以添加一个按钮作为AVPlayerViewController的子视图。通过这样做,您可以拦截完成按钮轻击手势。
由于此处似乎没有任何完美答案,因此您可以在某些情况下使用的解决方法是监视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
//*******
}
以下是我设法检测点击次数的代码。
在应用程序委托类中。但它会检测该视图中找到的每个按钮。你可以添加一些控件,检查标题。
-(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");
}
如果你有一个显示AVPlayerViewController
的视图控制器A,你可以检查VC A中的viewDidAppear / viewWillAppear。每当调用它们时,至少我们会知道AVPlayerViewController不再显示,因此不应该播放。
一种方法是通过搜索AVPlayerViewController子视图中的相应UIButton,为AVPlayerViewController上的现有“完成”按钮添加其他操作。找到按钮后,使用addTarget:action: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
}