AVPlayerLayer not bounds to UIView frame
Asked Answered
D

4

22

I'm trying to add AVPlayerLayer to UIView

    self.player = AVPlayer(URL: NSURL(fileURLWithPath: path!))
    self.playerLayer = AVPlayerLayer(player: player);
    self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    self.playerLayer.frame =  ctrVideo.bounds;
    self.ctrVideo.layer.addSublayer(playerLayer);
    player.play();

This is the video's container (in blue):

This is the view:

Constraints: Constraints:

Final result: enter image description here

I can't figure out why video is not bounds to UIVIew coordinates. If i bounds it into controller's superview, it is ok.

Dannie answered 16/2, 2016 at 21:31 Comment(5)
Sublayer won't resize automatically when view's frame changes, you have to do it manually.Leghorn
But why it's work when added to rootview?Dannie
Probably because the root view is already resized correctly at that time. Try printing rootview.frame and ctrVideo.frame when you add a sublayer, see if there is any difference.Leghorn
Yes, becouse i adding AVPlayerLayer in viewDidLoad event... Before constraints apply.Dannie
ok i'll move this to the answer.Leghorn
L
45

Sublayer won't resize automatically when view's frame changes, you have to do it manually. You can subclass a UIView to make it easier.

class VideoContainerView: UIView {
  var playerLayer: CALayer?
  override func layoutSublayersOfLayer(layer: CALayer) {
    super.layoutSublayersOfLayer(layer)
    playerLayer?.frame = self.bounds
  }
}

And then in your code:

self.player = AVPlayer(URL: NSURL(fileURLWithPath: path!))
self.playerLayer = AVPlayerLayer(player: player);
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.playerLayer.frame =  ctrVideo.bounds;
self.ctrVideo.layer.addSublayer(playerLayer);
self.ctrVideo.playerLayer = playerLayer;
player.play();

Just make sure that you change that class of your "ctrVideo" from UIView to VideoContainerView.

Leghorn answered 16/2, 2016 at 22:27 Comment(3)
update for swift 3: func layoutSublayersOfLayer(layer: CALayer) has been changed to func layoutSublayers(of layer: CALayer) {}Calculable
I was struggling with that player layer for some time. This works perfectly, thank you!Alisun
what if you create player after layoutSublayersOfLayer call?Polenta
N
1

Here's @almas' awesome answer in Swift 4

class VideoContainerView: UIView {    
    var playerLayer: CALayer?

    override func layoutSublayers(of layer: CALayer) {
        super.layoutSublayers(of: layer)
        playerLayer?.frame = self.bounds
    }
}
Newness answered 14/1, 2018 at 3:25 Comment(0)
D
0

I adding AVPlayerLayer in viewDidLoad event... Before constraints apply.

Dannie answered 16/2, 2016 at 22:24 Comment(0)
A
-1

Here is AVPlayerLayer-backed view, that tries to keep its aspect ratio.

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
Accomplice answered 14/3, 2017 at 19:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.