J'ai une UIView
qui contient une AVPlayer
pour montrer une vidéo. Lorsque je change d'orientation, je dois changer la taille et l'emplacement de la vidéo.
Je n'ai pas beaucoup d'expérience avec le redimensionnement des calques. J'ai donc du mal à redimensionner la vidéo.
Je commence par créer la AVPlayer
et en ajoutant son lecteur au calque de mon vidéoHolderView:
NSURL *videoURL = [NSURL fileURLWithPath:videoPath];
self.avPlayer = [AVPlayer playerWithURL:videoURL];
AVPlayerLayer* playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
playerLayer.frame = videoHolderView.bounds;
playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
playerLayer.needsDisplayOnBoundsChange = YES;
[videoHolderView.layer addSublayer:playerLayer];
videoHolderView.layer.needsDisplayOnBoundsChange = YES;
Ensuite, je modifie ultérieurement la taille et l'emplacement du cadre de videoHolderView:
[videoHolderView setFrame:CGRectMake(0, 0, 768, 502)];
À ce stade, j'ai besoin de l'avPlayer pour redimensionner à ces mêmes dimensions. Cela ne se produit pas automatiquement - avPlayer reste à sa petite taille dans videoHolderView.
Si quelqu'un peut aider, j'apprécierais vraiment tout conseil.
Merci les gars.
En fin de compte, j'ai résolu ce problème en rajoutant AVPlayerLayer à UIView. Je ne sais pas pourquoi la modification du cadre a supprimé le calque, mais c'est ce qui s'est passé. Voici la solution finale:
//amend the frame of the view
[videoHolderView setFrame:CGRectMake(0, 0, 768, 502)];
//reset the layer's frame, and re-add it to the view
AVPlayerLayer* playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
playerLayer.frame = videoHolderView.bounds;
[videoHolderView.layer addSublayer:playerLayer];
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFit;
En fait, vous ne devriez pas ajouter la couche d'AVPlayer en tant que sous-couche. Au lieu de cela, vous devez utiliser la méthode suivante, dans la sous-classe de la vue dans laquelle vous souhaitez afficher AVPlayer.
+ (Class)layerClass
{
return [AVPlayerLayer class];
}
Et utilisez la ligne suivante pour ajouter (définir) la couche de lecteur.
[(AVPlayerLayer *)self.layer setPlayer:self.avPlayer];
J'espère que ça aide;-)
Conversion de la solution de @ Suran en Swift 3:
Tout d’abord, créez une classe héritant de UIView où vous ne remplacez qu’une variable:
import UIKit
import AVFoundation
class AVPlayerView: UIView {
override class var layerClass: AnyClass {
return AVPlayerLayer.self
}
}
Ajoutez ensuite un simple UIView à l’aide du générateur d’interface et remplacez sa classe par celle que vous venez de créer: AVPlayerView
Ensuite, créez un point de vente pour cette vue. Appelez-le avPlayerView
@IBOutlet weak var avPlayerView: AVPlayerView!
Maintenant, vous pouvez utiliser cette vue dans votre viewcontroller et accéder à son avlayer comme ceci:
let avPlayer = AVPlayer(url: video)
let castedLayer = avPlayerView.layer as! AVPlayerLayer
castedLayer.player = avPlayer
avPlayer.play()
La couche suivra maintenant les contraintes comme le ferait une couche régulière. Pas besoin de changer manuellement les limites ou les tailles.
Vous pouvez changer l'image du calque en redéfinissant la méthode -layoutSubviews
:
- (void)layoutSubviews
{
[super layoutSubviews];
self.playerLayer.frame = self.bounds;
}
Trouvé un excellent article de Marco Santarossa qui montre plusieurs approches de la réparation. https://marcosantadev.com/calayer-auto-layout-Swift/
J'ai utilisé sa première suggestion pour réinitialiser le cadre de la couche lors de l'événement viewDidLayoutSubViews ().
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
playerLayer.frame = view.layer.bounds
}
la réponse de theDunc n'a pas fonctionné pour moi. J'ai trouvé une solution plus simple: je devais simplement ajuster le cadre de AVPlayerLayer après l'avoir modifié dans UIView:
avPlayerLayer.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
Dans ce blog est indiqué, le cadre d'une vue affecte également celui du calque qu'il contient.
Lorsque vous modifiez le cadre d’une vue, il vous suffit de changer celui du calque.
Pour ce cas, ce n'est pas vrai.
Pour arriver à playerLayer
, vous devez parcourir videoHolderView.layer.sublayers
et changer chacun.
c'est ce que j'ai fait dans Swift 2.2
if let sublayers = videoHolderView.layer.sublayers{
for layer in sublayers where layer is AVPlayerLayer{
layer.frame.size = ... // change size of the layer here
}
}
J'ai eu ce problème dans Swift 2.3, et j'ai résolu d'écrire une classe PlayerView appropriée et de le définir comme sous-vue:
import UIKit
import AVKit
import AVFoundation
class PlayerPreviewView: UIView {
override class func layerClass() -> AnyClass {
return AVPlayerLayer.self
}
var player: AVPlayer? {
get {
return playerLayer.player
}
set {
playerLayer.player = newValue
}
}
var playerLayer: AVPlayerLayer {
return layer as! AVPlayerLayer
}
override init(frame: CGRect) {
super.init(frame: frame)
playerLayer.videoGravity = AVLayerVideoGravityResize
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
playerLayer.videoGravity = AVLayerVideoGravityResize
}
}
Dans le ViewController de présentation:
private func play(asset asset: AVURLAsset){
let playerItem = AVPlayerItem(asset: asset)
player = AVPlayer(playerItem: playerItem)
player?.actionAtItemEnd = .None
player?.muted = true
playerPreviewView = PlayerPreviewView(frame: CGRectZero)
view.addSubview(playerPreviewView)
playerPreviewView.player = player
playerPreviewView.translatesAutoresizingMaskIntoConstraints = false
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[subview]-0-|", options: .DirectionLeadingToTrailing, metrics: nil, views: ["subview": playerPreviewView]))
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[subview]-0-|", options: .DirectionLeadingToTrailing, metrics: nil, views: ["subview": playerPreviewView]))
player?.play()
NSNotificationCenter.defaultCenter().addObserver(self,
selector: #selector(SplashScreenViewController.videoDidFinish),
name: AVPlayerItemDidPlayToEndTimeNotification,
object: nil)
}
Première étape: consultez la propriété de redimensionnement automatique d'UIView dans UIVIew.h
@property (nonatomic) UIViewAutoresizing autoresizingMask; // redimensionnement simple. la valeur par défaut est UIViewAutoresizingNone
Cette propriété correspond aux contrôles "ressorts et jambes de force" de IB, bien que des essais soient nécessaires pour obtenir les résultats souhaités.
Pour ceux d'entre vous qui ne sont concernés que par le redimensionnement de l'AVPlayer lors de la rotation du périphérique, vous pouvez modifier le cadre de votre couche dans la méthode viewWillTransition (à la taille: CGSize, avec coordinator: UIViewControllerTransitionCoordinator) comme indiqué ci-dessous.
//Swift 4
//Method in UIViewController
//Variable definitions are:
//self.layer is the AVPlayerLayer
//self.videoPlayerView is the view that the AVPlayerLayer is the sublayer of
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { context in
self.layer.layoutIfNeeded()
UIView.animate(
withDuration: context.transitionDuration,
animations: {
self.layer.frame = self.videoPlayerView.bounds
}
)
}, completion: nil)
}
Cela animera votre cadre de calque avec toutes les autres vues pendant la rotation.
Tous ceux qui recherchent la version Xamarin comme je le cherchais
n'ajoutez pas AVPlayerLayer en tant que sous-couche mais définissez la couche sur Avplayer Layer
[Export("layerClass")]
public static Class LayerClass()
{
return new Class(typeof(AVPlayerLayer));
}
La ligne suivante définit le calque
(this.Layer as AVPlayerLayer).Player = _player; // Acplayer