可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFit;
回答2:
Actually you shouldn't add AVPlayer's layer as a sublayer. Instead of that you should use the following method, in the subclass of view in which you want to display AVPlayer.
+ (Class)layerClass
{
return [AVPlayerLayer class];
}
And use the following line to add(set) the player layer.
[(AVPlayerLayer *)self.layer setPlayer:self.avPlayer];
Hope it helps;-)
回答3:
Converting @Suran's solution to Swift 3:
First, create a class inheriting UIView where you override only 1 variable:
import UIKit
import AVFoundation
class AVPlayerView: UIView {
override class var layerClass: AnyClass {
return AVPlayerLayer.self
}
}
Then add a simple UIView using the interface builder and change its class to the one you just created: AVPlayerView
Then, make an outlet for that view. Call it avPlayerView
@IBOutlet weak var avPlayerView: AVPlayerView!
Now, you can use that view inside your viewcontroller and access its avlayer like this:
let avPlayer = AVPlayer(url: video)
let castedLayer = avPlayerView.layer as! AVPlayerLayer
castedLayer.player = avPlayer
avPlayer.play()
The layer will now follow the constraints just like a regular layer would do. No need to manually change bounds or sizes.
回答4:
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];
回答5:
You can change the layer's frame by overriding the -layoutSubviews
method:
- (void)layoutSubviews
{
[super layoutSubviews];
self.playerLayer.frame = self.bounds;
}
回答6:
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
}
回答7:
theDunc's answer did not work for me. I found a solution that is more simple: I just needed to adjust the frame of the AVPlayerLayer after changing it in the UIView:
avPlayerLayer.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
In this blog is stated, that a View's frame also affect that of the layer in it.
When you change a view’s frame, it’s simply changing the layer’s frame.
For this case, it is not true.
回答8:
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.
回答9:
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
}
}
回答10:
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)
}
回答11:
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
回答12:
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.