swiftvideouikitavfoundationavplayer

AVPlayer not using full width


On larger phones my AVPlayer, which displays a video, does not take up the entire view width as expected. It works perfectly on my iPhone 15 pro but the iPhone 16 plus in the simulator is having difficulties. I have tried a lot of things to fix it, like setting constraints on the video but nothing seems to work. I have attached a screenshot and my code below. Thanks for your help in advance!

@IBOutlet weak var cover: UIView!
private var playerLayer: AVPlayerLayer?

    private func setupVideoCover() {[
        player = AVPlayer(url: videoURL)
        player?.isMuted = true
        player?.actionAtItemEnd = .none
 
        playerLayer = AVPlayerLayer(player: player)
        
        if let playerLayer = playerLayer {
            cover.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
            cover.layer.addSublayer(playerLayer)
        }

        NotificationCenter.default.addObserver(
            self,
            selector: #selector(restartVideoFromBeginning),
            name: .AVPlayerItemDidPlayToEndTime,
            object: player?.currentItem
        )
        
        player?.play()
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        playerLayer?.videoGravity = .resizeAspectFill
        playerLayer?.frame = cover.bounds.centered(in: cover.bounds)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        setupVideoCover()
    }

Video playback screen

enter image description here

enter image description here

enter image description here


Solution

  • As Apple's docs explains about viewDidLayoutSubviews:

    This method being called does not indicate that the individual layouts of the view's subviews have been adjusted. Each subview is responsible for adjusting its own layout.

    So when viewDidLayoutSubviews get called, your cover is not get its frame when laid out on screen yet. To set frame of playerLayer base on cover's bounds, the right way is using methods layoutSubviews of UIView:

    class ViewController: UIViewController {
        @IBOutlet weak var cover: CoverView! // <- change to CoverView
        ...
    }
    
    class CoverView: UIView {
        private var playerLayer: AVPlayerLayer?
        
        override func layoutSubviews() {
            super.layoutSubviews()
            playerLayer?.videoGravity = .resizeAspectFill
            //set frame here
            playerLayer?.frame = cover.bounds.centered(in: cover.bounds)
        }
    }
    

    In your case, your code works well on iPhone 15 pro because your xib file's device might be set to iPhone 15 pro, cover's frame intially base on xib file.