2012-02-29 11 views
13

Estoy desarrollando un reproductor de video con AVPlayer API de AV Foundation en MonoTouch (pero una solución en Object-c también podría ser agradable). Estoy tratando de implementar un modo de pantalla completa.Pantalla completa: AVPlayerLayer no se redimensiona al cambiar el tamaño de su vista principal

Para mostrar los fotogramas de vídeo, tengo un UIView (vamos a llamarlo vista de reproducción) en la que he añadido el AVPlayerLayer como subvista de la capa de vista de reproducción:

UIView *playbackView = ... 
AVPlayerLayer *playerLayer = ... 
[playbackView.layer addSublayer:playerLayer]; 

las propiedades de capa se establecen así:

playerLayer.needsDisplayOnBoundsChange = YES; 
playbackView.layer.needsDisplayOnBoundsChange = YES; 

para asegurarse de que cambian de tamaño si se cambia el tamaño de la vista de reproducción. Inicialmente, la vista de reproducción tiene las coordenadas (0, 0, 320, 180) (solo se muestra en la parte superior de la IU). Entonces, estoy haciendo una animación en pantalla completa ajustando el tamaño del marco vista de reproducción como el tamaño de la ventana:

playbackView.frame = playbackView.window.bounds; 

Es funciona bien. La vista de reproducción ahora está llenando toda la ventana (establece un color de fondo para verla). Pero mi AVPlayerLayer aún se mantiene en la parte superior de la vista como anteriormente en el modo de pantalla completa.

¿Por qué la capa AVPlayer no se redimensiona de acuerdo con la nueva vista? ¿Debo hacer una animación en AVPlayerLayer también? Una actualización con setNeedsLayout, layoutSubviews (no parece cambiar algo ...)? ¿Quitar AVPlayerLayer y agregarlo de nuevo?

+0

Una solución es para cambiar el tamaño también la trama de capa de acuerdo con el marco de vista de reproducción durante la animación (playerLayer.frame = playbackView.frame). Pero si hacemos eso, la capa no se anima sino que se muestra directamente con el tamaño final. ¿Cómo podemos animar también este cambio? Use Core Animation en lugar de animación UIView? – nicolas

Respuesta

25

No agregar como subcapa. Sólo tiene que utilizar la capa de playbackView como AVPlayerLayer, así:

+ (Class)layerClass { 
    return [AVPlayerLayer class]; 
} 
- (AVPlayer*)player { 
    return [(AVPlayerLayer *)[self layer] player]; 
} 
- (void)setPlayer:(AVPlayer *)player { 
    [(AVPlayerLayer *)[self layer] setPlayer:player]; 
} 

Ver AV Foundation Programming Guide - El jugador Ver

+0

¿Qué sucede si necesita agregar como una subcapa? Por ejemplo, usar una vista alojada en capas que contiene muchas subcapas, incluida una AVPlayerLayer. – Dalmazio

+3

Apple actualizó su ejemplo de AVPlayer con una versión que demuestra cómo implementar esto usando swift: https://developer.apple.com/library/prerelease/ios/samplecode/AVFoundationSimplePlayer-iOS/Listings/Swift_AVFoundationSimplePlayer_iOS_PlayerView_swift.html#//apple_ref/ doc/uid/TP40016103-Swift_AVFoundationSimplePlayer_iOS_PlayerView_swift-DontLinkElementID_14 – morgman

+0

¿Alguna idea de cómo hacer esto con un NSView?NSView no tiene 'layerClass' – iluvcapra

6

En caso de agregar AVPlayerLayer como subcapa, necesita actualizar su marco

- (void)viewDidLayoutSubviews { 
    [super viewDidLayoutSubviews]; 

    self.avPlayerLayer.frame = self.movieContainerView.bounds; 
} 
+0

mala idea. Debido a que es un jugador, cada vez que se actualiza el tiempo del indicador, esta función se envía en – jose920405

+0

wow ¡qué mala respuesta estoy atrapado! no hagas esto – mahieddine

+1

Buena respuesta, para mí fue muy útil – Genevios

0

Hay una mejor manera de lograr esto. Aquí está AVPlayerLayer -backed view, que está tratando de mantener la relación de aspecto del video. Simplemente use AutoLayout como siempre.

PlayerView.h:

@interface PlayerView : UIView 

@property (strong, nonatomic) AVPlayerLayer *layer; 

@end 

PlayerView.m:

@interface PlayerView() 

@property (strong, nonatomic) NSLayoutConstraint *aspectConstraint; 

@end 

@implementation PlayerView 

@dynamic layer; 

+ (Class)layerClass { 
    return [AVPlayerLayer class]; 
} 

- (instancetype)init { 
    self = [super init]; 
    if (self) { 
     self.userInteractionEnabled = NO; 

     [self addObserver:self forKeyPath:@"layer.videoRect" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; 
    } 
    return self; 
} 

- (void)dealloc { 
    [self removeObserver:self forKeyPath:@"layer.videoRect"]; 
} 

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { 
    if ([keyPath isEqualToString:@"layer.videoRect"]) { 
     CGSize size = [change[NSKeyValueChangeNewKey] CGRectValue].size; 
     if (change[NSKeyValueChangeNewKey] && size.width && size.height) { 
      self.aspectConstraint.active = NO; 
      self.aspectConstraint = [self.widthAnchor constraintEqualToAnchor:self.heightAnchor multiplier:size.width/size.height]; 
      self.aspectConstraint.priority = UILayoutPriorityDefaultHigh; 
      self.aspectConstraint.active = YES; 
     } 
     else { 
      self.aspectConstraint.active = NO; 
     } 
    } 
} 

@end 

Y la misma solución en Swift:

import UIKit 
import AVFoundation 

class PlayerView : UIView { 

    override var layer: AVPlayerLayer { 
     return super.layer as! AVPlayerLayer 
    } 

    override class var layerClass: AnyClass { 
     return AVPlayerLayer.self 
    } 

    var aspectConstraint: NSLayoutConstraint? 

    override init(frame: CGRect) { 
     super.init(frame: frame) 
     self.isUserInteractionEnabled = false 
     self.addObserver(self, forKeyPath:"layer.videoRect", options:[.initial, .new], context:nil) 
    } 

    required init?(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
     self.isUserInteractionEnabled = false 
     self.addObserver(self, forKeyPath:"layer.videoRect", options:[.initial, .new], context:nil) 
    } 

    deinit { 
     self.removeObserver(self, forKeyPath: "layer.videoRect") 
    } 

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 
     if (keyPath == "layer.videoRect") { 
      if let rect = change?[.newKey] as? NSValue { 
       let size = rect.cgRectValue.size 
       if (size.width > 0 && size.height > 0) { 
        self.aspectConstraint?.isActive = false 
        self.aspectConstraint = self.widthAnchor.constraint(equalTo: self.heightAnchor, multiplier:size.width/size.height) 
        self.aspectConstraint?.priority = UILayoutPriorityDefaultHigh 
        self.aspectConstraint?.isActive = true 
       } 
       else { 
        self.aspectConstraint?.isActive = false 
       } 
      } 
     } 
    } 
} 
Cuestiones relacionadas