Уровень AVPlayer внутри представления не изменяется при изменении рамок UIView
У меня есть UIView
, который содержит AVPlayer
для показа видео. При изменении ориентации мне нужно изменить размер и расположение видео.
Я не очень разбираюсь в изменении размеров слоев, поэтому у меня возникают проблемы с изменением размера видео.
Я начинаю с создания AVPlayer
и добавления его игрока в свой слой videoHolderView:
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;
Затем, в более позднем пункте, я изменяю размер и расположение кадра videoHolderView:
[videoHolderView setFrame:CGRectMake(0, 0, 768, 502)];
В этот момент мне нужно, чтобы avPlayer изменил размер до такого же размера. Это происходит не автоматически - avPlayer остается в этом небольшом размере в видеохолдинге.
Если кто-то может помочь, я бы очень признателен за любые советы.
Спасибо, ребята.
Ответы
Ответ 1
В конце концов я решил это, повторно добавив AVPlayerLayer в UIView. Я не уверен, почему смена рамки удалила слой, но это произошло. Здесь окончательное решение:
//amend the frame of the view
[videoHolderView setFrame:CGRectMake(0, 0, 768, 502)];
//reset the layer frame, and re-add it to the view
AVPlayerLayer* playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
playerLayer.frame = videoHolderView.bounds;
[videoHolderView.layer addSublayer:playerLayer];
Ответ 2
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFit;
Ответ 3
Преобразование решения @Suran в Swift:
Сначала создайте класс, наследующий UIView, где вы переопределяете только 1 переменную:
import UIKit
import AVFoundation
class AVPlayerView: UIView {
override class var layerClass: AnyClass {
return AVPlayerLayer.self
}
}
Затем добавьте простой UIView с помощью построителя интерфейса и измените его класс на тот, который вы только что создали: AVPlayerView
![change class of regular UIView to AVPlayerView]()
Затем сделайте выход для этого представления. Назовите это avPlayerView
@IBOutlet weak var avPlayerView: AVPlayerView!
Теперь вы можете использовать это представление внутри вашего viewcontroller и получить доступ к его avlayer следующим образом:
let avPlayer = AVPlayer(url: video)
let castedLayer = avPlayerView.layer as! AVPlayerLayer
castedLayer.player = avPlayer
avPlayer.play()
Теперь слой будет следовать ограничениям, как обычный слой. Нет необходимости вручную изменять границы или размеры.
Ответ 4
На самом деле вам не следует добавлять слой AVPlayer в качестве подуровня. Вместо этого вы должны использовать следующий метод в подклассе вида, в котором вы хотите отображать AVPlayer.
+ (Class)layerClass
{
return [AVPlayerLayer class];
}
И используйте следующую строку, чтобы добавить (установить) слой игрока.
[(AVPlayerLayer *)self.layer setPlayer:self.avPlayer];
Надеюсь, что это поможет; -)
Ответ 5
Нашел отличную статью Марко Сантаросса, в которой показано несколько подходов к исправлению. https://marcosantadev.com/calayer-auto-layout-swift/
Я использовал его первое предложение для сброса фрейма слоя во время события viewDidLayoutSubViews().
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
playerLayer.frame = view.layer.bounds
}
Ответ 6
Вы можете изменить кадр слоя, переопределив метод -layoutSubviews
:
- (void)layoutSubviews
{
[super layoutSubviews];
self.playerLayer.frame = self.bounds;
}
Ответ 7
Ответ на Dunc не работал у меня. Я нашел решение, которое было более простым: мне просто нужно было отрегулировать рамку AVPlayerLayer после ее изменения в UIView:
avPlayerLayer.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
В этом блоге указано, что рамка просмотра также влияет на рамку в этом слое.
Когда вы меняете рамку представлений, просто меняя рамку слоев.
В этом случае это неверно.
Ответ 8
У меня была эта проблема в Swift 2.3, и я решил написать правильный класс PlayerView и установить его как 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
}
}
В представлении 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)
}
Ответ 9
Первый шаг: проверьте свойство URIiew autoresizing в UIVIew.h
@property (неатомный) UIViewAutoresizing autoresizingMask;//простое изменение размера. default - UIViewAutoresizingNone
Это свойство соответствует элементам "пружины и стойки" в IB, хотя вам потребуются некоторые эксперименты, чтобы получить нужные результаты.
Ответ 10
Чтобы добраться до playerLayer
, вам нужно пройти через videoHolderView.layer.sublayers
и изменить каждый.
это то, что я сделал в 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
}
}
Ответ 11
Любой, кто ищет версию Xamarin, когда я искал
не добавляйте AVPlayerLayer в качестве подуровня, но установите слой в слой Avplayer
[Export("layerClass")]
public static Class LayerClass()
{
return new Class(typeof(AVPlayerLayer));
}
Следующая строка устанавливает слой
(this.Layer as AVPlayerLayer).Player = _player; // Acplayer
Ответ 12
Для тех из вас, кто занимается только изменением размера AVPlayer во время поворота устройства, вы можете изменить кадр слоя в методе viewWillTransition (по размеру: CGSize, с координатором: UIViewControllerTransitionCoordinator), как показано ниже.
//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)
}
Это будет анимировать ваш слой слоя вместе со всеми другими видами во время вращения.
Ответ 13
В Swift 4 используйте
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
или любое другое свойство AVLayerVideoGravity
, которое вы пожелаете.