web-dev-qa-db-fra.com

Le calque AVPlayer dans une vue ne redimensionne pas lorsque le cadre UIView est modifié

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.

47
theDuncs

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];
15
theDuncs
  playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFit;
59
Markinson

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

20
Suran

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

 change class of regular UIView to 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.

19
Sam

Vous pouvez changer l'image du calque en redéfinissant la méthode -layoutSubviews:

- (void)layoutSubviews
{
    [super layoutSubviews];
    self.playerLayer.frame = self.bounds;
}
6
Dave Batton

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
}
4
PaulMJax

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.

2
frot.io

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
    }
}
1
Misha Kouznetsov

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)
}
1
Giordano Scalzo

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.

1
Jay Wardell

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.

0
jikigi

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
0
Fahad Rehman