web-dev-qa-db-ja.com

AVPlayerViewControllerのコントロールを非表示-開始時のみ

AVPlayerViewController.showsPlaybackControlsをfalseに設定すると、コントロールはまったく表示されません。画面をタップしても。

コントロールを非表示にして開始したいが、それでもタップしてそれらを呼び出すことができます。上記のプロパティをtrueに設定すると、それらは表示され始めます。 (はい、数秒後に色あせます。)非表示で開始する方法はありますか?

27
Andrew Duncan

更新:カスタマイズを改善するために独自のコントロールを作成しました。それはより困難ですが、時間の価値があります。 Appleのサンプルコードを参照してください。 PiPの実装とカスタムコントロールの作成についてです: https://developer.Apple.com/library/prerelease/ios/samplecode/AVFoundationPiPPlayer/Introduction/Intro.html


更新:タップすると、AVPlayerViewControllerはtouchesBeganイベントのみを発生させ、touchesEndedイベントは発生させません。ただし、コントロールを表示するだけで十分です。

まず、コントロールを非表示にする必要があります。 AVPlayerViewControllerを提示する直前にこのコードを配置してください

YourAVPlayerViewController.showsPlaybackControls = false

次に、AVPlayerViewControllerをサブクラス化し、この関数を追加します。

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

    self.showsPlaybackControls = true

    super.touchesBegan(touches, withEvent: event)
}

古い解決策:

これを解決しました。主なアイデアは、AVPlayerViewControllerの上にUIViewを配置して、タップジェスチャを取得し、不要になったUIViewを非表示にすることです。

コードは次のとおりです。

import AVKit
import UIKit

// Create a custom AVPlayerViewController
@available(iOS 8.0, *)
final class CustomAVPlayerViewController: AVPlayerViewController {

    // Create a UIView to put on top of all
    lazy var topView = UIView(frame: CGRectMake(0, 0, width, height))

    override func viewDidLoad() {
        super.viewDidLoad()

        // For sure, set it to clearcolor
        // (DON'T set alpha = 0 because it will stop receiving user interaction)
        topView.backgroundColor = UIColor.clearColor()

        // Add it to the view of AVPlayerViewController
        self.view.addSubview(topView)

        // Bring it to front
        self.view.bringSubviewToFront(topView)

        // Add a tap gesture recognizer
        topView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "handleTap"))
    }

    // Handle the tap
    func handleTap() {

        // Show the control
        self.showsPlaybackControls = true

        // Hide the topView. You can unhide it when needed later.
        self.topView.hidden = true
    }
}

コントロールを非表示にする必要がある場合は、次を実行します。

var AVViewController = CustomAVPlayerViewController()

...

// Hide controls
AVViewController.showsPlaybackControls = false
// Show topView
AVViewController.topView.hidden = false
18
thegathering

動的なジェスチャー認識機能の関係を使用してこれを解決したと思います。このソリューションは、カスタムコントロールを(一貫性のために)回避し、パブリックAPIのみを使用し、AVPlayerViewController(他の回答に記載されているように明示的に禁止されています)をサブクラス化しません。

方法は次のとおりです。

  1. AVPlayerViewControllerを埋め込むコンテナビューコントローラを作成します。 (これは、コントロールに関係なく便利です。再生ロジックをどこかに配置する必要があるためです。)

  2. 最初にshowsPlaybackControlsfalseに設定します。

  3. UITapGestureRecognizerを追加して、最初のタップを認識します。

  4. ジェスチャ認識エンジンのアクションメソッドで、showsPlaybackControlstrueに設定します。

  5. これまでのところは機能しますが、コントロールは最初のタップですぐに消えます。これを修正するには、ジェスチャ認識エンジンのデリゲートとして自分を設定し、gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:およびその他のシングルタップジェスチャレコグナイザに対してtrueを返します。

Swiftでの実際の実装は次のとおりです。 andreyvit/ModalMoviePlayerViewController 最新のコードのレポを確認してください:

import UIKit
import AVKit
import AVFoundation

public class ModalMoviePlayerViewController: UIViewController {

    private let fileName: String
    private let loop: Bool

    private var item: AVPlayerItem!
    private var player: AVPlayer!
    internal private(set) var playerVC: AVPlayerViewController!
    private var waitingToAutostart = true

    public init(fileName: String, loop: Bool = true) {
        self.fileName = fileName
        self.loop = loop
        super.init(nibName: nil, bundle: nil)
    }

    public required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    public override func viewDidLoad() {
        super.viewDidLoad()

        let url = NSBundle.mainBundle().URLForResource(fileName, withExtension: nil)!

        item = AVPlayerItem(URL: url)

        player = AVPlayer(playerItem: item)
        player.actionAtItemEnd = .None
        player.addObserver(self, forKeyPath: "status", options: [], context: nil)

        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ModalMoviePlayerViewController.didPlayToEndTime), name: AVPlayerItemDidPlayToEndTimeNotification, object: item)

        playerVC = AVPlayerViewController()
        playerVC.player = player
        playerVC.videoGravity = AVLayerVideoGravityResizeAspectFill
        playerVC.showsPlaybackControls = false

        let playerView = playerVC.view
        addChildViewController(playerVC)
        view.addSubview(playerView)
        playerView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        playerView.frame = view.bounds
        playerVC.didMoveToParentViewController(self)

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ModalMoviePlayerViewController.handleTap))
        tapGesture.delegate = self
        view.addGestureRecognizer(tapGesture)
    }

    deinit {
        player.pause()
        player.removeObserver(self, forKeyPath: "status")
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }

    func togglePlayPause() {
        if isPlaying {
            pause()
        } else {
            play()
        }
    }

    func restart() {
        seekToStart()
        play()
    }

    func play() {
        if player.status == .ReadyToPlay {
            player.play()
        } else {
            waitingToAutostart = true
        }
    }

    func pause() {
        player.pause()
        waitingToAutostart = false
    }

    var isPlaying: Bool {
        return (player.rate > 1 - 1e-6) || waitingToAutostart
    }

    private func performStateTransitions() {
        if waitingToAutostart && player.status == .ReadyToPlay {
            player.play()
        }
    }

    public override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        performStateTransitions()
    }

    @objc func didPlayToEndTime() {
        if isPlaying && loop {
            seekToStart()
        }
    }

    private func seekToStart() {
        player.seekToTime(CMTimeMake(0, 10))
    }

    public override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if !playerVC.showsPlaybackControls {
            playerVC.showsPlaybackControls = true
        }
        super.touchesBegan(touches, withEvent: event)
    }

}

extension ModalMoviePlayerViewController: UIGestureRecognizerDelegate {

    @IBAction func handleTap(sender: UIGestureRecognizer) {
        if !playerVC.showsPlaybackControls {
            playerVC.showsPlaybackControls = true
        }
    }

    /// Prevents delivery of touch gestures to AVPlayerViewController's gesture recognizer,
    /// which would cause controls to hide immediately after being shown.
    ///
    /// `-[AVPlayerViewController _handleSingleTapGesture] goes like this:
    ///
    ///     if self._showsPlaybackControlsView() {
    ///         _hidePlaybackControlsViewIfPossibleUntilFurtherUserInteraction()
    ///     } else {
    ///         _showPlaybackControlsViewIfNeededAndHideIfPossibleAfterDelayIfPlaying()
    ///     }
    public func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailByGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        if !playerVC.showsPlaybackControls {
            // print("\nshouldBeRequiredToFailByGestureRecognizer? \(otherGestureRecognizer)")
            if let tapGesture = otherGestureRecognizer as? UITapGestureRecognizer {
                if tapGesture.numberOfTouchesRequired == 1 {
                    return true
                }
            }
        }
        return false
    }

}
10

収集の答えは良いです。代わりにtouchesCancelledをオーバーライドして、コントロールが表示されてから再び非表示にならないようにします。

override public func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
    super.touchesCancelled(touches, withEvent: event)

    // toggle the player controls on if they were set to off
    if !self.showsPlaybackControls {
        self.showsPlaybackControls = true
    }
}
2
Kellen Styler

Swift 3でそれを行う簡単な方法は、myController.showsPlaybackControls = false、およびプレーヤービュー全体をボタンまたはジェスチャレコグナイザーでオーバーレイします。これをストーリーボード上の別のコントローラーの別のビューに埋め込み、これを簡単にし、プレーヤーのコントローラーをオーバーライドしません。トリックは、一度クリックした後にボタンを非表示にすることです。これは、プレーヤーコントローラーがその後タップを追跡してコントロールを表示/非表示にするためです。

@IBAction func enableControls(button:UIButton)
{
    controller?.showsPlaybackControls = true
    button.isHidden = true //The button is only needed once, then the player takes over.
}
1
Peter DeWeese