Hide controls in AVPlayerViewController — only at start

前端 未结 4 1167
孤街浪徒
孤街浪徒 2020-12-25 11:59

If you set AVPlayerViewController.showsPlaybackControls to false, the controls will not show at all. Even if you tap the screen.

I want the controls to start out hid

4条回答
  •  Happy的楠姐
    2020-12-25 12:43

    I think I've solved this using dynamic gesture recognizer relationships. The solution avoids custom controls (for consistency), uses only public API and does not subclass AVPlayerViewController (which is explicitly disallowed, as noted in other answers).

    Here's how:

    1. Make a container view controller that embeds AVPlayerViewController. (This is useful regardless of the controls, because you need to put the playback logic somewhere.)

    2. Set showsPlaybackControls to false initially.

    3. Add a UITapGestureRecognizer to recognize the initial tap.

    4. In the action method for the gesture recognizer, set showsPlaybackControls to true.

    5. So far, it would work, but the controls would disappear immediately on that initial tap. To fix that, set yourself as a delegate for the gesture recognizer, implement gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer: and return true for any other single-tap gesture recognizer.

    Here's the actual implementation in Swift; check andreyvit/ModalMoviePlayerViewController repo for the latest 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) {
            performStateTransitions()
        }
    
        @objc func didPlayToEndTime() {
            if isPlaying && loop {
                seekToStart()
            }
        }
    
        private func seekToStart() {
            player.seekToTime(CMTimeMake(0, 10))
        }
    
        public override func touchesBegan(touches: Set, 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
        }
    
    }
    

提交回复
热议问题