ios 在 AVPlayerViewController 中隐藏控件——仅在开始时

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29090186/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 05:13:41  来源:igfitidea点击:

Hide controls in AVPlayerViewController -- only at start

iosvideoavplayer

提问by Andrew Duncan

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

如果将 AVPlayerViewController.showsPlaybackControls 设置为 false,则控件根本不会显示。即使你点击屏幕。

I want the controls to start out hidden, but still be able to summon them by tapping. If I set the mentioned property to true, they start out visible. (Yes they fade after a few seconds.) Is there a way to start hidden, but still be accessible?

我希望控件一开始是隐藏的,但仍然可以通过点击来召唤它们。如果我将提到的属性设置为 true,它们开始可见。(是的,它们会在几秒钟后消失。)有没有办法开始隐藏,但仍然可以访问?

回答by thegathering

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

更新:我最终制作了自己的控件以实现更好的自定义。这更困难,但值得花时间。请阅读 Apple 的示例代码以供参考。这是关于实现画中画,但也关于制作自定义控件: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.

更新:点击时, AVPlayerViewController 只触发 touchesBegan 事件,而不触发 touchesEnded 事件。但这足以显示控件。

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

首先,您需要隐藏控件。把这段代码放在你展示 AVPlayerViewController 之前

YourAVPlayerViewController.showsPlaybackControls = false

Then subclass AVPlayerViewController and add this function:

然后子类化 AVPlayerViewController 并添加这个函数:

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.

我刚刚解决了这个问题。主要思想是在 AVPlayerViewController 之上放置一个 UIView 以恢复点击手势,并在不再需要时隐藏该 UIView。

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

回答by Andrey Tarantsov

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).

我想我已经使用动态手势识别器关系解决了这个问题。该解决方案避免了自定义控件(为了一致性),仅使用公共 API 并且不进行子类化AVPlayerViewController(如其他答案中所述,这是明确禁止的)。

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 showsPlaybackControlsto falseinitially.

  3. Add a UITapGestureRecognizer to recognize the initial tap.

  4. In the action method for the gesture recognizer, set showsPlaybackControlsto 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 truefor any other single-tap gesture recognizer.

  1. 制作一个嵌入AVPlayerViewController. (无论控件如何,这都很有用,因为您需要将播放逻辑放在某处。)

  2. 设置showsPlaybackControlsfalse初始。

  3. 添加一个 UITapGestureRecognizer 来识别初始点击。

  4. 在手势识别器的动作方法中,设置showsPlaybackControlstrue

  5. 到目前为止,它可以工作,但控件会在初始点击时立即消失。要解决这个问题,请将自己设置为手势识别器的代理,实现gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:并返回true任何其他单击手势识别器。

Here's the actual implementation in Swift; check andreyvit/ModalMoviePlayerViewControllerrepo for the latest code:

这是 Swift 中的实际实现;检查andreyvit/ModalMoviePlayerViewControllerrepo 以获取最新代码:

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
    }

}

回答by Peter DeWeese

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.

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

回答by Kellen Styler

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

thegathering 的回答是好的。我会改写 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
    }
}