Hide controls in AVPlayerViewController — only at start

前端 未结 4 1164
孤街浪徒
孤街浪徒 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条回答
  • 2020-12-25 12:37

    A simple way to do it in Swift 3 is to set myController.showsPlaybackControls = false, and to overlay the whole player view with a button or gesture recognizer. I embed it into another view in another controller on a storyboard to make this simple and to not override the player controller. The trick then is to hide the button after being clicked once, because the player controller will thereafter track taps to show/hide the controls.

    @IBAction func enableControls(button:UIButton)
    {
        controller?.showsPlaybackControls = true
        button.isHidden = true //The button is only needed once, then the player takes over.
    }
    
    0 讨论(0)
  • 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<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
        }
    
    }
    
    0 讨论(0)
  • 2020-12-25 12:53

    UPDATE: I ended up making my own controls for better customization. It's more difficult but worth the time. Please read Apple's sample code for reference. It's about implementing PiP but also about making custom controls: https://developer.apple.com/library/prerelease/ios/samplecode/AVFoundationPiPPlayer/Introduction/Intro.html


    UPDATE: When tapped, AVPlayerViewController only fires touchesBegan event, and not touchesEnded event. But it's enough to show the controls.

    First you need to hide the control. Put this code right before you present AVPlayerViewController

    YourAVPlayerViewController.showsPlaybackControls = false
    

    Then subclass AVPlayerViewController and add this function:

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

    OLD SOLLUTION:

    I've just solved this. The main idea is to put a UIView on top of the AVPlayerViewController to reveive tap gesture, and hide that UIView when it is no longer needed.

    Here's the 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
        }
    }
    

    And when you need to hide the controls, do this:

    var AVViewController = CustomAVPlayerViewController()
    
    ...
    
    // Hide controls
    AVViewController.showsPlaybackControls = false
    // Show topView
    AVViewController.topView.hidden = false
    
    0 讨论(0)
  • 2020-12-25 12:58

    thegathering's answer is good. I would override touchesCancelled instead so that the controls do not show and then hide again.

    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
        }
    }
    
    0 讨论(0)
提交回复
热议问题