web-dev-qa-db-fra.com

Masquer les contrôles dans AVPlayerViewController - uniquement au démarrage

Si vous définissez AVPlayerViewController.showsPlaybackControls sur false, les contrôles ne s'afficheront pas du tout. Même si vous appuyez sur l'écran.

Je veux que les commandes commencent cachées, mais je puisse toujours les invoquer en appuyant sur. Si je mets la propriété mentionnée à true, ils commencent visibles. (Oui, ils s'estompent après quelques secondes.) Existe-t-il un moyen de commencer caché, mais toujours accessible?

27
Andrew Duncan

MISE À JOUR: J'ai fini par faire mes propres contrôles pour une meilleure personnalisation. C'est plus difficile mais ça vaut le coup. Veuillez lire l'exemple de code d'Apple pour référence. Il s'agit d'implémenter PiP mais aussi de faire des contrôles personnalisés: https://developer.Apple.com/library/prerelease/ios/samplecode/AVFoundationPiPPlayer/Introduction/Intro.html


MISE À JOUR: Lorsque vous appuyez dessus, AVPlayerViewController ne déclenche que l'événement touchesBegan, et non l'événement touchEnded. Mais il suffit de montrer les commandes.

Vous devez d'abord masquer le contrôle. Mettez ce code juste avant de présenter AVPlayerViewController

YourAVPlayerViewController.showsPlaybackControls = false

Ensuite, sous-classe AVPlayerViewController et ajoutez cette fonction:

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

    self.showsPlaybackControls = true

    super.touchesBegan(touches, withEvent: event)
}

ANCIENNE SOLLUTION:

Je viens de résoudre ça. L'idée principale est de mettre une UIView au-dessus de l'AVPlayerViewController pour réactiver le geste de tapotement et de masquer cette UIView lorsqu'elle n'est plus nécessaire.

Voici le code:

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
    }
}

Et lorsque vous devez masquer les contrôles, procédez comme suit:

var AVViewController = CustomAVPlayerViewController()

...

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

Je pense que j'ai résolu cela en utilisant des relations de reconnaissance de gestes dynamiques. La solution évite les contrôles personnalisés (pour la cohérence), utilise uniquement l'API publique et ne sous-classe pas AVPlayerViewController (ce qui est explicitement interdit, comme indiqué dans d'autres réponses).

Voici comment:

  1. Créez un contrôleur de vue de conteneur qui incorpore AVPlayerViewController. (Ceci est utile quels que soient les contrôles, car vous devez placer la logique de lecture quelque part.)

  2. Définissez showsPlaybackControls sur false initialement.

  3. Ajoutez un UITapGestureRecognizer pour reconnaître le tap initial.

  4. Dans la méthode d'action pour la reconnaissance de mouvement, définissez showsPlaybackControls sur true.

  5. Jusqu'à présent, cela fonctionnerait, mais les commandes disparaîtraient immédiatement lors de ce tap initial. Pour résoudre ce problème, définissez-vous en tant que délégué pour la reconnaissance des gestes, implémentez gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer: et renvoyez true pour tout autre outil de reconnaissance gestuelle à simple pression.

Voici l'implémentation réelle dans Swift; vérifier andreyvit/ModalMoviePlayerViewController repo pour le dernier code:

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
Andrey Tarantsov

la réponse de thegathering est bonne. Je remplacerais plutôt touchesCancelled à la place pour que les contrôles ne s'affichent pas, puis se cachent à nouveau.

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

Un moyen simple de le faire dans Swift 3 est de définir myController.showsPlaybackControls = false, et pour superposer la vue entière du lecteur avec un bouton ou un lecteur de gestes. Je l'ai intégré dans une autre vue dans un autre contrôleur sur un storyboard pour rendre cela simple et pour ne pas remplacer le contrôleur du joueur. L'astuce consiste alors à masquer le bouton après avoir été cliqué une fois, car le contrôleur du lecteur suivra ensuite les taps pour afficher/masquer les commandes.

@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