我在自定义AVPlayerViewController
的{{1}}方法中创建了AVPlayer
和附加的viewDidAppear
。但是,当我按下“完成”按钮时,我的自定义视图控制器会自动关闭。
我想拦截这个动作以便使用我自己的放松塞格,但我不知道该如何做到这一点。我找到了UIViewController
但不是MPMoviePlayerViewController
的示例。
我为AVPlayerViewController
找到的代码如下:
MPMoviePlayerViewController
我向Apple询问了这个问题,他们回答如下:
感谢您与Apple开发者技术支持(DTS)联系。我们的 工程师已经审查了您的请求并得出结论 没有支持的方法来实现所需的功能 目前正在发货系统配置。
答案 0 :(得分:5)
我将AVPlayerViewController子类化,并发布了一个来自viewWillDisappear的通知,表示解雇了AVPlayerViewController。
- (void) viewWillDisappear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] postNotificationName:kPlayerViewDismissedNotification object:nil];
[super viewWillDisappear:animated];
}
这可能不是100%正确(因为如果你在AVPlayerViewController上显示另一个视图会失败),但它对我有效,因为AVPlayerViewController总是在堆栈顶部。
答案 1 :(得分:5)
Apple没有提供处理完成按钮的内置方式这一事实令人失望。
我不想从AVPlayerViewController继承,因为Apple不支持它,并且可能会在下一个iOS更新中打开一堆蠕虫。
我的解决方法是每200毫秒启动一次定时器并检查以下情况:
if (playerVC.player.rate == 0 &&
(playerVC.isBeingDismissed || playerVC.nextResponder == nil)) {
// Handle user Done button click and invalidate timer
}
播放器的rate
属性为0表示视频不再播放。如果视图控制器被解雇或已被解雇,我们可以安全地假设用户单击了“完成”按钮。
答案 2 :(得分:4)
我通过保持对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()
}
}
答案 3 :(得分:3)
你可以通过继承AVPLayerViewController来实现。但由于这个原因,存在一些未定义的行为。 (在Apple文档中给出。见下文) 我也试过继承AVPlayerViewController,我遇到了内存问题。
根据Apple文档:
不要继承AVPlayerViewController。覆盖这个类 方法不受支持,导致未定义的行为。
目前,当AVPlayerViewController被解除时,它们不提供回调。 请参阅Apple开发人员论坛:
在thread1中,Apple的家伙说:
我仍然相信管理AVPlayerViewController的退出 通过利用上面推荐的手势方法手动实例 将是知道玩家视图控制器的最可靠方式 已被解雇,因为你将负责解雇 那一点
希望它有所帮助!
嗯,有一个问题的解决方法。您可以添加一个按钮作为AVPlayerViewController的子视图。通过这样做,您可以拦截完成按钮轻击手势。
答案 4 :(得分:2)
我不确定这是否对您的用例有用,但是如果您可以针对iOS 12 +,AVPlayerViewController确实提供了委托属性/协议(AVPlayerViewControllerDelegate
)以及一些可能有用的相关委托方法(请参阅下文) :
特别是在iOS 12+上:
func playerViewController(_ playerViewController: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator)
在tvOS 11+上:
func playerViewControllerShouldDismiss(_ playerViewController: AVPlayerViewController) -> Bool
func playerViewControllerWillBeginDismissalTransition(_ playerViewController: AVPlayerViewController)
func playerViewControllerDidEndDismissalTransition(_ playerViewController: AVPlayerViewController)
答案 5 :(得分:0)
由于此处似乎没有任何完美的答案,您可以在某些情况下使用的解决方法是监控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
//*******
}
答案 6 :(得分:0)
以下是我设法检测点击次数的代码。
在应用程序委托类中。但它会检测该视图中找到的每个按钮。你可以添加一些控件,检查类似的标题。
-(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");
}
答案 7 :(得分:0)
如果您有一个显示AVPlayerViewController
的视图控制器A,您可以检查VC A中的viewDidAppear / viewWillAppear。每当调用它们时,至少我们会知道AVPlayerViewController不再显示,因此不应该玩。
答案 8 :(得分:0)
不敢相信没有人会介绍最明显的解决方案:只需发出通知或执行AVPlayerViewController
的{{1}}方法中需要执行的任何操作即可。 请不要持有对其的-dealloc
引用,否则将不会调用strong
。
如果您不破坏内部逻辑(例如,不从覆盖的方法中调用-dealloc
),则AVPlayerViewController
的子类化没有问题(即使文档另有说明)。多年来(自iOS 8发布以来),我一直在使用以下代码,而没有任何运行时或AppStore提交问题:
super
因此,要回答该问题,只需将其添加到@interface VideoPlayerViewController : AVPlayerViewController
@end
@implementation VideoPlayerViewController
- (UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskLandscape; }
@end
// now use VideoPlayerViewController instead of AVPlayerViewController
子类中:
AVPlayerViewController
如果您真的不敢继承类,请使用智能技术,将关联的对象附加到- (void)dealloc {
// player was dismissed and is going to die, do any cleanup now
}
,您可以控制其AVPlayerViewController
,请参见:https://stackoverflow.com/a/19344475
要评论其他解决方案:
-dealloc
的{{1}}变成AVPlayer
并检查rate
的{{1}}:聪明,但是在玩家暂停时失败事先(当然,可以使用计时器代替)0.0f
的{{1}}:在播放器顶部显示某些内容时可能会失败,例如内置字幕选择器。一种类似的解决方案是在呈现视图控制器的AVPlayerViewController
中检查玩家是否曾经出现过并且应该100%地工作。答案 9 :(得分:-1)
一种方法是向现有的" Done"添加额外的操作。通过搜索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
}