Готово событие нажатия кнопки в AVPlayerViewController

Я хочу воспроизвести локальное видео в AVPlayerViewController, но не нашел событие нажатия кнопки "Готово".

Мое видео может воспроизводиться в AVPlayerViewController, но я не нашел следующую кнопку, предыдущую кнопку и событие готового нажатия кнопки.

5 ответов


Я сделал это, чтобы получить Done событие нажатия кнопки от AVPlayerViewController,

Прежде всего, создайте расширение Notification.Name как ниже

extension Notification.Name {
static let kAVPlayerViewControllerDismissingNotification = Notification.Name.init("dismissing")
}

Теперь создайте расширение AVPlayerViewController и переопределить viewWillDisappear как ниже

// create an extension of AVPlayerViewController
extension AVPlayerViewController {
    // override 'viewWillDisappear'
    open override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // now, check that this ViewController is dismissing
        if self.isBeingDismissed == false {
            return
        }

        // and then , post a simple notification and observe & handle it, where & when you need to.....
        NotificationCenter.default.post(name: .kAVPlayerViewControllerDismissingNotification, object: nil)
    }
}

ЭТО ТОЛЬКО ДЛЯ ОСНОВНОЙ ФУНКЦИОНАЛЬНОСТИ, КОТОРАЯ ОБРАЩАЕТСЯ К СОБЫТИЮ КНОПОК.

счастливое кодирование...

Официально не происходит событие нажатия кнопки "Готово", об этом здесь сказал инженер из Apple.

Что касается моего исследования, я обнаружил, что есть один, но действительно косвенный способ получить событие, если нажать кнопку "Готово".

Я обнаружил, что единственная переменная AVPlayerViewController, которая изменяется при нажатии кнопки "Готово", AVPlayerViewController.view.frame, Прежде всего view.frame появляется в центре viewController.

Если вы представите это с animated : true это идет к нижней части viewController и обратно к центру. Когда готово будет нажата, он возвращается к основанию.

Если вы представите это с animated : false будет только 2 изменения: рамка будет в центре viewController, когда вы начнете воспроизводить видео, и внизу, когда нажмете "Готово".

Так что, если вы добавите наблюдателя к AVPlayerViewController.view.frame в обратном вызове present(PlayerViewController, animated : true) Вы получите только один вызов наблюдателя, прямо после нажатия кнопки "Готово", и просмотр видео будет вне экрана.

В моем случае AVPlayerViewController был представлен модально с анимацией. Код ниже работал для меня:

Swift 3.0

override func viewDidLoad()
{
    super.viewDidLoad()

    let videoURL = NSURL(fileURLWithPath:Bundle.main.path(forResource: "MyVideo", ofType:"mp4")!)
    let player = AVPlayer(url: videoURL as URL)

    present(playerViewController, animated: false) { () -> Void in
        player.play()

        self.playerViewController.player = player
        self.playerViewController.addObserver(self, forKeyPath: #keyPath(UIViewController.view.frame), options: [.old, .new], context: nil)
    }}
    override func observeValue(forKeyPath keyPath: String?,
                           of object: Any?,
                           change: [NSKeyValueChangeKey : Any]?,
                           context: UnsafeMutableRawPointer?)
{

    print(self.playerViewController.view.frame)
    //Player view is out of the screen and
    //You can do your custom actions
}

Кроме того, я узнал, когда вы нажимаете Готово, AVPlayerViewController не закрывается, и вы можете увидеть его в ParentViewController.presentedViewController, поэтому вы не можете добавить наблюдателя к этому свойству

Объективная версия c: (на основе ответа vmchar)

- (void)viewDidLoad
{
    [super viewDidLoad];
        NSURL *url = [NSURL fileURLWithPath:path];
        AVPlayer *player = [AVPlayer playerWithURL:url];
        AVPlayerViewController *AVPlayerVc = [[AVPlayerViewController alloc] init];
        if (AVPlayerVc) {
            [self presentViewController:AVPlayerVc animated:YES completion:^{
                [player play];
                AVPlayerVc.player = player;
                [AVPlayerVc addObserver:self forKeyPath:@"view.frame" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) context:nil];
            }];

        }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"view.frame"]) {
        CGRect newValue = [change[NSKeyValueChangeNewKey]CGRectValue];
        CGFloat y = newValue.origin.y;
        if (y != 0) {
              NSLog(@"Video Closed");
        }
     }
}

Самым простым решением для меня было создание подкласса AVPlayerViewController и добавить простой блок завершения к нему.

class MYVideoController: AVPlayerViewController {

  typealias DissmissBlock = () -> Void
  var onDismiss: DissmissBlock?

  override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    if isBeingDismissed {
      onDismiss?()
    }
  }
}

использование

...
let avPlayerViewController = MYVideoController()
avPlayerViewController.onDismiss = { [weak self] in 
    print("dismiss")
}

Это небольшое улучшение по сравнению с ответом @vmchar:

Мы можем использовать метод.isBeingDismissed, чтобы гарантировать, что AVPlayerViewController закрывается вместо анализа кадра.

...

let videoURL = NSURL(fileURLWithPath:"video.mp4")
let player = AVPlayer(url: videoURL as URL)

present(playerViewController!, animated: false) { () -> Void in
    player.play()

    self.playerViewController!.player = player
    self.playerViewController!.addObserver(self, forKeyPath:#keyPath(UIViewController.view.frame), options: [.old, .new], context: nil)
...

Затем соблюдать значение

override func observeValue(forKeyPath keyPath: String?,
                           of object: Any?,
                           change: [NSKeyValueChangeKey : Any]?,
                           context: UnsafeMutableRawPointer?)
{

    if (playerViewController!.isBeingDismissed) {  
     // Video was dismissed -> apply logic here
    } 
} 

Я нашел обходной путь, не создавая подкласс AVPlayerViewController, что в документации четко указано, что вы не должны этого делать ( https://developer.apple.com/documentation/avkit/avplayerviewcontroller). Это некрасиво, но он выполнил свою работу за меня, предоставив мне место для запуска кода, когдаAVPlayerViewController уволен.

AnyViewController: UIViewController, AVPlayerViewControllerDelegate {
    private let playerVC = AVPlayerViewController()

    override func viewDidLoad() {
        playerVC.delegate = self        
    }

    func playerViewController(_ playerViewController: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) {
        if playerViewController.isBeingDismissed {
            playerViewController.dismiss(animated: false) { [weak self] in
                self?.someDismissFunc()
            }
        }
    }
}

Насколько я могу судить, это действительно запутано, что нет чистого способа сделать это:(

Я думаю, что есть много способов снять шкуру с кошки. Мне нужно было обработать событие клика "Готово". В моем случае я хотел убедиться, что я смог подключиться к событию после нажатия кнопки "X" и AVPlayerViewController был полностью закрыт (отклонен).

Swift 4.0

protocol TableViewCellVideoPlayerViewControllerDelegate: class {
    func viewControllerDismissed()
}

class MyPlayerViewController: AVPlayerViewController {

    weak var navDelegate: TableViewCellVideoPlayerViewControllerDelegate?

    open override func viewDidDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        if self.isBeingDismissed {
            self.navDelegate?.viewControllerDismissed()
        }
    }
}

class TodayCell: UITableViewCell, SFSafariViewControllerDelegate,  TableViewCellVideoPlayerViewControllerDelegate {
    func viewControllerDismissed() {
        self.continueJourney()
    }
 }

У меня была та же проблема, и я нашел другой прием (работает с YoutubePlayer Swift Framework, который воспроизводит видео в веб-просмотре, но вы можете адаптировать его для других целей).

В моем случае нажатие Done кнопку и нажав Pause Кнопка на полноэкранном видеоплеере приводит к pause событие на YoutubePlayer. Однако, поскольку видео воспроизводится в другом окне, я переклассифицировал главное окно своего приложения и переопределил becomeKey а также resignKey функции для хранения, является ли мое окно ключевым окном или нет, вот так:

class MyWindow:UIWindow {
    override func becomeKey() {
        Services.appData.myWindowIsKey = true
    }

    override func resignKey() {
        Services.appData.myWindowIsKey = false
    }
}

После этого я могу проверить, является ли мое окно ключевым, когда состояние видео переходит в режим паузы - когда Done кнопка была нажата, мое окно является ключевым окном, и я могу закрыть свой контроллер просмотра видео, и в Pause В этом случае мое окно не является ключевым, поэтому я ничего не делаю.

Вы можете заглянуть в этот пост Stackru, и вот проект github для справки. Вам придется использовать это:

 self.showsPlaybackControls = true

Пожалуйста, ознакомьтесь с документацией Apple.

Другие вопросы по тегам