Swift - 如何根据视频播放器帧的大小动态调整 UIView 的大小
Swift - how to resize a UIView dynamically based on the size of a video player frame
我有一个视频播放器和一个 UIView
覆盖它,我在视频区域中用于 Gesture Recognition
。我这样做是为了让您可以点击视频的不同区域来播放、倒带和执行其他功能。
目前,这在横向模式下工作正常,但如果我将设备旋转到纵向模式。 UIView
不会调整大小,但视频播放器会调整。我尝试以编程方式使用约束,但我不知道如何访问视频播放器的 NSLayout
锚点。
我有一个函数可以检索我当前用来将 UIView
设置为视频播放器大小的视频帧的大小。
只需将下面的代码复制到项目中即可播放视频并显示我要调整的UIView
。您只需将 UIView
添加到 UIViewController
故事板。该代码不包含 GestureRecogniser 部分。
import UIKit
import AVFoundation
class ViewController: UIViewController {
let controlContainerView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(white: 0, alpha: 1)
return view
}()
let activityIndicatorView: UIActivityIndicatorView = {
let aiv = UIActivityIndicatorView(style: .whiteLarge)
aiv.translatesAutoresizingMaskIntoConstraints = false
aiv.startAnimating()
return aiv
}()
@IBOutlet weak var videoContainer: UIView!
var player: AVPlayer?
var playerLayer: AVPlayerLayer?
var tapRegionBounds: UIView!
var tapRegionAreaCreated: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let urlString = "https://www.radiantmediaplayer.com/media/bbb-360p.mp4"
videoPlayer(filename: urlString)
player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)
}
override func viewDidAppear(_ animated: Bool) {
self.playerLayer?.frame = CGRect(x: 0, y: 0, width: self.videoContainer.frame.width, height: self.videoContainer.frame.height)
updateTapRegions()
}
func videoPlayer(filename: String){
let item = AVPlayerItem(url: URL(string: filename)!)
player = AVPlayer(playerItem: item)
playerLayer = AVPlayerLayer(player: player)
playerLayer?.videoGravity = .resizeAspect
playerLayer?.frame = CGRect(x: 0, y: 0, width: self.videoContainer.frame.width, height: self.videoContainer.frame.height)
videoContainer.layer.addSublayer(playerLayer!)
player?.play()
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "currentItem.loadedTimeRanges" {
activityIndicatorView.stopAnimating()
controlContainerView.backgroundColor = .clear
}
}
func videoFrame() -> CGRect? {
guard let layer = playerLayer
else { return nil }
let frame = layer.videoRect
if frame.height > 0.0 {
let frameInWindow = layer.convert(frame, to: nil)
return view.convert(frameInWindow, from: nil)
} else {
return nil
}
}
//THE CODE BELOW KIND OF WORKS TO SHOW HOW I WANT IT TO WORK BUT WITHOUT HAVING TO DELETE THE VIEW EACH TIME
func updateTapRegions() {
guard let video = videoFrame() else { return }
let container = videoContainer.bounds
if tapRegionAreaCreated == true {
tapRegionBounds.removeFromSuperview()
tapRegionAreaCreated = false
}
tapRegionBounds = UIView()
tapRegionBounds.frame = CGRect(x: video.minX, y: video.minY, width: container.width, height: video.height)
tapRegionBounds?.alpha = 0.5
tapRegionBounds.backgroundColor = UIColor.red
if tapRegionAreaCreated == false {
view.addSubview(tapRegionBounds)
tapRegionAreaCreated = true
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { (context) in
}) { (context) in
self.playerLayer?.frame = CGRect(x: 0, y: 0, width: self.videoContainer.frame.width, height: self.videoContainer.frame.height)
self.updateTapRegions()
}
}
}
我尝试更新 viewWillLayoutSubviews()
中的框架,但也没有任何改变。我知道我的 updateTapRegions 函数不是正确的方法,但如果有人能指出正确的方向,那将非常感谢。
最后,我通过将代码从转换函数(即使视频播放器调整大小正确)移动到 viewDidLayoutSubviews 来解决这个问题,然后工作正常。此外,我不再在每次设备改变方向时创建和删除视图。
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.playerLayer?.frame = CGRect(x: 0, y: 0, width: self.videoContainer.frame.width, height: self.videoContainer.frame.height)
guard let video = videoFrame() else { return }
if tapRegionBounds != nil {
self.tapRegionBounds.frame = CGRect(x: video.minX, y: video.minY, width: video.width, height: video.height)
}
}
不幸的是,您没有在问题中说明如何调整所有这一切所依赖的视频容器视图的大小。但这是一种可能性。我使用自动布局在纵向和横向中获得这些结果:
现在假设我们有一个子层(一个 AVPlayerLayer)和一个子视图(点击手势视图)。好吧,可以使用自动布局调整子视图的大小以自动适应!所以只剩下播放器层;它不会使用自动布局自动调整大小,但您可以在 viewDidLayoutSubviews
.
中手动调整大小
我有一个视频播放器和一个 UIView
覆盖它,我在视频区域中用于 Gesture Recognition
。我这样做是为了让您可以点击视频的不同区域来播放、倒带和执行其他功能。
目前,这在横向模式下工作正常,但如果我将设备旋转到纵向模式。 UIView
不会调整大小,但视频播放器会调整。我尝试以编程方式使用约束,但我不知道如何访问视频播放器的 NSLayout
锚点。
我有一个函数可以检索我当前用来将 UIView
设置为视频播放器大小的视频帧的大小。
只需将下面的代码复制到项目中即可播放视频并显示我要调整的UIView
。您只需将 UIView
添加到 UIViewController
故事板。该代码不包含 GestureRecogniser 部分。
import UIKit
import AVFoundation
class ViewController: UIViewController {
let controlContainerView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(white: 0, alpha: 1)
return view
}()
let activityIndicatorView: UIActivityIndicatorView = {
let aiv = UIActivityIndicatorView(style: .whiteLarge)
aiv.translatesAutoresizingMaskIntoConstraints = false
aiv.startAnimating()
return aiv
}()
@IBOutlet weak var videoContainer: UIView!
var player: AVPlayer?
var playerLayer: AVPlayerLayer?
var tapRegionBounds: UIView!
var tapRegionAreaCreated: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let urlString = "https://www.radiantmediaplayer.com/media/bbb-360p.mp4"
videoPlayer(filename: urlString)
player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)
}
override func viewDidAppear(_ animated: Bool) {
self.playerLayer?.frame = CGRect(x: 0, y: 0, width: self.videoContainer.frame.width, height: self.videoContainer.frame.height)
updateTapRegions()
}
func videoPlayer(filename: String){
let item = AVPlayerItem(url: URL(string: filename)!)
player = AVPlayer(playerItem: item)
playerLayer = AVPlayerLayer(player: player)
playerLayer?.videoGravity = .resizeAspect
playerLayer?.frame = CGRect(x: 0, y: 0, width: self.videoContainer.frame.width, height: self.videoContainer.frame.height)
videoContainer.layer.addSublayer(playerLayer!)
player?.play()
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "currentItem.loadedTimeRanges" {
activityIndicatorView.stopAnimating()
controlContainerView.backgroundColor = .clear
}
}
func videoFrame() -> CGRect? {
guard let layer = playerLayer
else { return nil }
let frame = layer.videoRect
if frame.height > 0.0 {
let frameInWindow = layer.convert(frame, to: nil)
return view.convert(frameInWindow, from: nil)
} else {
return nil
}
}
//THE CODE BELOW KIND OF WORKS TO SHOW HOW I WANT IT TO WORK BUT WITHOUT HAVING TO DELETE THE VIEW EACH TIME
func updateTapRegions() {
guard let video = videoFrame() else { return }
let container = videoContainer.bounds
if tapRegionAreaCreated == true {
tapRegionBounds.removeFromSuperview()
tapRegionAreaCreated = false
}
tapRegionBounds = UIView()
tapRegionBounds.frame = CGRect(x: video.minX, y: video.minY, width: container.width, height: video.height)
tapRegionBounds?.alpha = 0.5
tapRegionBounds.backgroundColor = UIColor.red
if tapRegionAreaCreated == false {
view.addSubview(tapRegionBounds)
tapRegionAreaCreated = true
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { (context) in
}) { (context) in
self.playerLayer?.frame = CGRect(x: 0, y: 0, width: self.videoContainer.frame.width, height: self.videoContainer.frame.height)
self.updateTapRegions()
}
}
}
我尝试更新 viewWillLayoutSubviews()
中的框架,但也没有任何改变。我知道我的 updateTapRegions 函数不是正确的方法,但如果有人能指出正确的方向,那将非常感谢。
最后,我通过将代码从转换函数(即使视频播放器调整大小正确)移动到 viewDidLayoutSubviews 来解决这个问题,然后工作正常。此外,我不再在每次设备改变方向时创建和删除视图。
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.playerLayer?.frame = CGRect(x: 0, y: 0, width: self.videoContainer.frame.width, height: self.videoContainer.frame.height)
guard let video = videoFrame() else { return }
if tapRegionBounds != nil {
self.tapRegionBounds.frame = CGRect(x: video.minX, y: video.minY, width: video.width, height: video.height)
}
}
不幸的是,您没有在问题中说明如何调整所有这一切所依赖的视频容器视图的大小。但这是一种可能性。我使用自动布局在纵向和横向中获得这些结果:
现在假设我们有一个子层(一个 AVPlayerLayer)和一个子视图(点击手势视图)。好吧,可以使用自动布局调整子视图的大小以自动适应!所以只剩下播放器层;它不会使用自动布局自动调整大小,但您可以在 viewDidLayoutSubviews
.