Swift: Resize an AVPlayerLayer to match the bounds of its parent container
Asked Answered
C

3

8

I have a local video playing using AVPlayer and AVPlayerLayer (Swift, iOS 8.2), and i need to resize the AVPlayerLayer (introPlayerLayer) when its parent view (introView) is resized, including on orientation change. So far nothing I have tried seems to work and the playerLayer has the wrong size and position.

The AVPlayer is set up using a local mp4 video, and the AVPlayerLayer is initialized using that player. The layer is then added as a sublayer to the parent view.

Here is my code:

func playIntro() {
    let path = NSBundle.mainBundle().pathForResource("intro", ofType: "mp4")
    let url  = NSURL.fileURLWithPath(path!)
    let introPlayer           = AVPlayer(URL: url)
    introPlayer.allowsExternalPlayback = false

    var introPlayerLayer = AVPlayerLayer(player: introPlayer)
    introView.layer.addSublayer(introPlayerLayer)
    introPlayerLayer.frame = introView.bounds

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerDidFinishPlaying:", name: AVPlayerItemDidPlayToEndTimeNotification, object: introPlayer.currentItem)

    introView.hidden = false
    introPlayer.play()
}

Should I be adding the AVPlayerLayer as a sublayer of the UIView, or should I be subclassing the parent container (UIView) as shown in the "The Player View" section of https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/02_Playback.html. If so, how would I write that PlayerView subclass (shown in Objective-C) in Swift? Thank you in advance for any assistance.

Chessy answered 12/3, 2015 at 1:6 Comment(2)
introPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill :)Savory
@Savory - Thank you!Lovable
K
11

Swift 3.

private func playVideo() {
        guard let path = Bundle.main.path(forResource: "video", ofType:"mp4") else {
            debugPrint("video.m4v not found")
            return
        }
        let player = AVPlayer(url: URL(fileURLWithPath: path))
        var playerLayer: AVPlayerLayer?
        playerLayer = AVPlayerLayer(player: player)
        playerLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
        playerLayer!.frame = self.view.frame
        self.view!.layer.addSublayer(playerLayer!)
        player.play()

    }
Kokaras answered 9/6, 2017 at 0:20 Comment(2)
Doesn't even show my player when using a subview and not the main view.Neuter
Good answer. Thanks.Rogue
O
5

I was able to do what you are trying to do, I think, in my own project. This is without subclassing. Here is my code:

override func viewWillLayoutSubviews() {
   previewScene()
}

func previewScene() {
    var firstAsset: AVAsset!, secondAsset: AVAsset!, thirdAsset: AVAsset!


     firstAsset = MediaController.sharedMediaController.s3Shot1
     secondAsset = MediaController.sharedMediaController.s3Shot2
     thirdAsset  = MediaController.sharedMediaController.s3Shot3


    if firstAsset != nil && secondAsset != nil && thirdAsset != nil {
        let assets = [firstAsset, secondAsset, thirdAsset]
        var shots = [AVPlayerItem]()

        for item in assets {
            let shot = AVPlayerItem(asset: item)
            shots.append(shot)
        }

        self.videoPlayer = AVQueuePlayer(items: shots)
        self.playerLayer = AVPlayerLayer(player: self.videoPlayer)
        self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
         AVPlayerItemDidPlayToEndTimeNotification, object: self.videoPlayer.items().last)
        self.videoPreviewLayer.layer.addSublayer(playerLayer)
        self.playerLayer.frame = self.videoPreviewLayer.bounds

    }

}
Obsess answered 14/11, 2015 at 21:23 Comment(3)
videoPreviewLayer is a rectangular view inside of my view controller.Obsess
@IBOutlet weak var videoPreviewLayer: UIView!Obsess
Can suggest me here: #54745859Holomorphic
W
3
introView.layer.masksToBounds = true

may also be helpful

Wildeyed answered 9/6, 2016 at 15:20 Comment(2)
Could you expand on this answer at all? stackoverflow.com/help/how-to-answerMorningglory
@RichBenner I had a problem with getting my video layer to stay within the bounds of the parent layer. I used self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill to fill the frame but also needed masksToBounds = true to keep it contained within the parent layer's bounds. I'm not a seasoned iOS dev but it worked for me.Wildeyed

© 2022 - 2024 — McMap. All rights reserved.