AVPlayer layer inside a view does not resize when UIView frame changes

前端 未结 13 845
小蘑菇
小蘑菇 2020-12-23 17:08

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.

<
相关标签:
13条回答
  • 2020-12-23 17:41

    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)
    }
    
    0 讨论(0)
  • 2020-12-23 17:42

    Any one searching for Xamarin Version as i was searching

    don't add the AVPlayerLayer as sublayer but set the layer to Avplayer Layer

    [Export("layerClass")]
    public static Class LayerClass()
    {
        return new Class(typeof(AVPlayerLayer));
    }
    

    The following line set the layer

    (this.Layer as AVPlayerLayer).Player = _player;   // Acplayer
    
    0 讨论(0)
  • 2020-12-23 17:43

    In the end I solved this by re-adding the AVPlayerLayer to the UIView. I'm not sure why changing the frame removed the layer, but it did. Here's the final solution:

    //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];
    
    0 讨论(0)
  • 2020-12-23 17:48

    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.

    0 讨论(0)
  • 2020-12-23 17:49

    In Swift 4 use

        playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
    

    or whichever property of AVLayerVideoGravity you desire.

    0 讨论(0)
  • 2020-12-23 17:52

    You can change the layer's frame by overriding the -layoutSubviews method:

    - (void)layoutSubviews
    {
        [super layoutSubviews];
        self.playerLayer.frame = self.bounds;
    }
    
    0 讨论(0)
提交回复
热议问题