AVPlayerViewControllerでローカルビデオを再生したいのですが、[完了]ボタンのクリックイベントが見つかりませんでした。
私のビデオはAVPlayerViewControllerで再生できますが、次のボタン、前のボタン、完了したボタンのクリックイベントが見つかりませんでした。
公式には、完了ボタンのクリックイベントはありません。Appleからのエンジニアがそれについて述べました here )。
私の調査に関しては、完了ボタンがクリックされた場合にイベントを取得するための、しかし間接的な方法が1つあることを知りました。
完了ボタンをクリックすると変化するAVPlayerViewControllerの唯一の変数はAVPlayerViewController.view.frame
であることがわかりました。まず最初に、view.frameがviewControllerの中央に表示されます。
animated : true
で表示すると、viewControllerの下部に移動し、中央に戻ります。クリックすると下に戻ります。
animated : false
で表示すると、2つの変更のみが行われます。フレームは、ビデオの再生を開始するとviewControllerの中央になり、下部では[完了]をクリックすると表示されます。
したがって、present(PlayerViewController, animated : true)
へのコールバックのAVPlayerViewController.view.frame
にオブザーバーを追加すると、完了ボタンをクリックしたときにオブザーバーが1回だけ呼び出され、ビデオビューが画面外になります。
私の場合、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
に表示されるため、このプロパティにオブザーバーを追加できないことがわかりました
これは、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)
}
}
これはISボタンのイベントを処理するための基本的な機能のみです。
ハッピーコーディング...
これは、@ 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
をサブクラス化し、それに単純な完了ブロックを追加することでした。
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")
}
猫の皮をむく方法はたくさんあると思います。 「完了」クリックイベントを処理する必要がありました。私の場合、「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()
}
}
私は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()
}
}
}
}
私が言うことができる限り、これを行うきれいな方法がないのは本当にめちゃくちゃです:(
私は同じ問題を抱えていて、別のトリックを見つけました(YoutubePlayer Swiftフレームワークで動作しますが、他の用途に適応させることができるかもしれません)。
私の場合、Done
ボタンを押し、フルスクリーンビデオプレーヤーのPause
ボタンを押すと、どちらもYoutubePlayerでpause
イベントが発生します。ただし、ビデオが別のウィンドウで再生されるので、アプリケーションのメインウィンドウをサブクラス化し、becomeKey
関数とresignKey
関数をオーバーライドして、ウィンドウがキーウィンドウかどうかを保存しました。
class MyWindow:UIWindow {
override func becomeKey() {
Services.appData.myWindowIsKey = true
}
override func resignKey() {
Services.appData.myWindowIsKey = false
}
}
それができたら、ビデオの状態が一時停止になったときにウィンドウがキーであるかどうかを確認できます。Done
ボタンを押すと、ウィンドウisキーウィンドウであり、閉じることができます私のビデオビューコントローラー、およびPause
の場合、私のウィンドウis notキーウィンドウなので、何もしません。