ios - avplayer swift 4
Ocultar controles en AVPlayerViewController: solo al inicio (4)
Si configura AVPlayerViewController.showsPlaybackControls en falso, los controles no se mostrarán en absoluto. Incluso si toca la pantalla.
Quiero que los controles comiencen ocultos, pero aún así poder invocarlos tocando. Si configuro la propiedad mencionada como verdadera, comienzan a ser visibles. (Sí, se desvanecen después de unos segundos). ¿Hay alguna forma de comenzar oculto, pero aún así ser accesible?
ACTUALIZACIÓN: terminé haciendo mis propios controles para una mejor personalización. Es más difícil pero vale la pena el tiempo. Por favor, lea el código de muestra de Apple para referencia. Se trata de implementar PiP, pero también de hacer controles personalizados: https://developer.apple.com/library/prerelease/ios/samplecode/AVFoundationPiPPlayer/Introduction/Intro.html
ACTUALIZACIÓN: cuando se toca, AVPlayerViewController solo activa el evento touchesBegan y no el evento touchesEnded. Pero es suficiente para mostrar los controles.
Primero necesitas ocultar el control. Coloque este código justo antes de presentar AVPlayerViewController
YourAVPlayerViewController.showsPlaybackControls = false
Luego, subclase AVPlayerViewController y agregue esta función:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.showsPlaybackControls = true
super.touchesBegan(touches, withEvent: event)
}
SOLUCIÓN VIEJA:
Acabo de resolver esto. La idea principal es colocar una UIView encima de AVPlayerViewController para reverenciar el gesto de toque y ocultar esa UIView cuando ya no sea necesaria.
Aquí está el código:
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
}
}
Y cuando necesite ocultar los controles, haga esto:
var AVViewController = CustomAVPlayerViewController()
...
// Hide controls
AVViewController.showsPlaybackControls = false
// Show topView
AVViewController.topView.hidden = false
Creo que he resuelto esto usando relaciones dinámicas de reconocimiento de gestos. La solución evita controles personalizados (por coherencia), usa solo API pública y no subclase AVPlayerViewController
(que está explícitamente prohibido, como se indica en otras respuestas).
Así es cómo:
Cree un controlador de vista de contenedor que incorpore
AVPlayerViewController
. (Esto es útil independientemente de los controles, porque necesita colocar la lógica de reproducción en alguna parte).Establecer
showsPlaybackControls
enfalse
inicialmente.Agregue un UITAPGestureRecognizer para reconocer el toque inicial.
En el método de acción para el reconocedor de gestos, establezca
showsPlaybackControls
entrue
.Hasta ahora, funcionaría, pero los controles desaparecerían inmediatamente en ese toque inicial. Para solucionarlo,
gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:
como un delegado para el reconocedor de gestos, implementegestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:
y devuelvatrue
para cualquier otro reconocedor de gestos de un solo toque.
Aquí está la implementación real en Swift; revise el andreyvit/ModalMoviePlayerViewController para el último código:
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
}
}
Una forma simple de hacerlo en Swift 3 es establecer myController.showsPlaybackControls = false
, y superponer toda la vista del jugador con un botón o un reconocedor de gestos. Lo incrusto en otra vista en otro controlador en un guión gráfico para hacerlo simple y no anular el controlador del reproductor. El truco consiste entonces en ocultar el botón después de hacer clic una vez, ya que el controlador del reproductor seguirá los toques para mostrar / ocultar los controles.
@IBAction func enableControls(button:UIButton)
{
controller?.showsPlaybackControls = true
button.isHidden = true //The button is only needed once, then the player takes over.
}
la respuesta de thegathering es buena. Anularía toques cancelados en su lugar para que los controles no se muestren y luego se oculten de nuevo.
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
}
}