AVPlayer layer inside a view does not resize when

2019-01-31 11:12发布

I have a UIView which contains an AVPlayer to show a video. When changing orientation, I need to change the size and location of the video.

I'm not very experienced with resizing layers, so I'm having problems making the video resize.

I start by creating the AVPlayer and adding its player to my videoHolderView's layer:

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;

Then, at a later point, I change the size and location of the videoHolderView's frame:

[videoHolderView setFrame:CGRectMake(0, 0, 768, 502)];

At this point, I need the avPlayer to resize to these same dimension. This doesn't happen automatically - the avPlayer stays at it's small size within the videoHolderView.

If anyone can help, I'd really appreciate any advice.

Thanks guys.

12条回答
我只想做你的唯一
2楼-- · 2019-01-31 11:53

Found a great article by Marco Santarossa that shows multiple approaches to fixing. https://marcosantadev.com/calayer-auto-layout-swift/

I used his first suggestion to reset the layer frame during viewDidLayoutSubViews() event.

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    playerLayer.frame = view.layer.bounds
}
查看更多
淡お忘
3楼-- · 2019-01-31 12:00

I had this problem in Swift 2.3, and I solved writing a proper PlayerView class and setting it as subview:

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
    }

}

In the presenting ViewController:

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)
}
查看更多
ら.Afraid
4楼-- · 2019-01-31 12:04

To get to playerLayer, you need to loop through videoHolderView.layer.sublayers and change each one.

this is what I did in 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
    }
}
查看更多
Juvenile、少年°
5楼-- · 2019-01-31 12:05
  playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFit;
查看更多
时光不老,我们不散
6楼-- · 2019-01-31 12:05

For those of you who are only concerned with resizing the AVPlayer during device rotation, you can alter your layer frame in the viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) method as shown below.

//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)
}

This will animate your layer frame along with all the other views during the rotation.

查看更多
够拽才男人
7楼-- · 2019-01-31 12:07

First step: check out UIView's autoresizing property in UIVIew.h

@property(nonatomic) UIViewAutoresizing autoresizingMask; // simple resize. default is UIViewAutoresizingNone

This property correspondents to the "springs and struts" controls in IB, though it will take you some experimentation to get the results you want.

查看更多
登录 后发表回答