UITableViewCell 内的 AVPlayer 与其他视图自动布局
AVPlayer inside UITableViewCell with other Views auto layout
我有一个 UITableViewCell,它有一个 UILabel 和一个播放我需要实现的在线视频的 AVPlayer。问题是 Apple gives 的 AVPlayer 无法使用自动布局来正确显示视频。
我相信有一些方法可以实现混合约束和框架布局的 UIView。不过,我认为这个问题还有更多内容:视频未在初始化时加载这一事实似乎存在问题。
UITableViewCell
import UIKit
import AVKit
import AVFoundation
class PlayerView: UIView {
var player: AVPlayer? {
get {
return playerLayer.player
}
set {
playerLayer.videoGravity = .resizeAspect
playerLayer.player = newValue
}
}
var playerLayer: AVPlayerLayer {
return layer as! AVPlayerLayer
}
override static var layerClass: AnyClass {
return AVPlayerLayer.self
}
}
class CustomTableCell: UITableViewCell {
let titleView = UILabel()
let containerView = UIView()
let playerView = PlayerView()
required init?(coder aDecoder: NSCoder) {super.init(coder: aDecoder)}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
titleView.text = "Title"
titleView.translatesAutoresizingMaskIntoConstraints = false
contentView.clipsToBounds = true
containerView.clipsToBounds = true
contentView.addSubview(containerView)
containerView.translatesAutoresizingMaskIntoConstraints = false
//contentView.layoutMargins = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
var lg = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: lg.topAnchor),
containerView.leadingAnchor.constraint(equalTo: lg.leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: lg.trailingAnchor),
containerView.bottomAnchor.constraint(equalTo: lg.bottomAnchor)
])
containerView.addSubview(titleView)
containerView.layoutMargins = UIEdgeInsets(top: 15, left: 17, bottom: 15, right: 17)
lg = containerView.layoutMarginsGuide
playerView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(playerView)
NSLayoutConstraint.activate([
titleView.topAnchor.constraint(equalTo: lg.topAnchor),
titleView.leadingAnchor.constraint(equalTo: lg.leadingAnchor),
titleView.trailingAnchor.constraint(equalTo: lg.trailingAnchor),
// titleView.bottomAnchor.constraint(equalTo: lg.bottomAnchor),
playerView.topAnchor.constraint(equalTo: titleView.bottomAnchor),
playerView.leadingAnchor.constraint(equalTo: lg.leadingAnchor),
playerView.trailingAnchor.constraint(equalTo: lg.trailingAnchor),
playerView.bottomAnchor.constraint(equalTo: lg.bottomAnchor)
])
}
}
我在我的视图控制器中设置了一个 UITableView,但重点是 UITableViewDataSource cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReuseableCell(withIdentifier: "cell") as! CustomTableCell
let url = NSURL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let avPlayer = AVPlayer(url: url! as URL)
cell.playerView.playerLayer.player = avPlayer
return cell
}
但是我没有看到视频。显然,有一个类似 willLayoutSubviews 的函数或类似的函数,但是我如何打破标题视图的约束以使用 AVPlayerLayer 或类似的东西进行约束,以便我可以看到视频?
一般来说,我如何查看具有其他自动布局视图的自定义 UITableViewCell 中的 AVPlayer 或任何播放器?
编辑 1:@ugur 给出的答案是正确的,因为 AVPlayer 需要调整大小。使用 leadngAnchor 和 trailingAnchor 扩展视图无法解决问题,因为扩展的只是当前视图的大小。
使用 centerXAnchor、widthAnchor 和 heightAnchor 都可以。 centerXAnchor 将使您的玩家在视图中居中。
您需要告诉自动布局如何计算 playerView 高度。
您可以尝试以下选项:
为 playerView 设置恒定高度
playerView.heightAnchor.constraint(equalToConstant: 100)
或设置 playerView 的纵横比
playerView.heightAnchor.constraint(equalTo: playerView.widthAnchor, multiplier: 1.0)
所以自动布局将知道如何定位您的 playerView。否则 playerView 高度将为 0.
建议:
不要在 cellFor
中创建 AVPlayer
而是在 cell init 或 awakeFromNib
中创建(如果使用故事板)。所以 AVPlayer
也可以重复使用。只需使用新 url 或 playerItem
重置设置即可。您还可以根据您的情况使用播放或暂停。但是不要在每次调用 cellFor
时都重新创建 AVPlayer
。
所以你这里的主要问题是你无法在单元格中看到你的播放器。我已经修改了你们中的一些代码,然后它也会显示和播放视频。
注意:这是非常基本的代码,我相信您将能够根据您的要求进一步改进它。
您的 PlayerView
保持不变,变化如下。我添加了新方法 configureFor
,它只会获取 url 作为参数并设置播放器。
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableCell
let url = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
cell.configureFor(url!)
return cell
}
以下是您的单元格代码。
在这里,我已将 UI 设置代码移动到一个通用函数 commonInit()
,然后按如下方式使用它。
class CustomTableCell: UITableViewCell {
let titleView = UILabel()
let containerView = UIView()
let playerView = PlayerView()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
func commonInit() {
titleView.text = "Title"
titleView.translatesAutoresizingMaskIntoConstraints = false
contentView.clipsToBounds = true
containerView.clipsToBounds = true
containerView.backgroundColor = .orange
contentView.addSubview(containerView)
containerView.translatesAutoresizingMaskIntoConstraints = false
//contentView.layoutMargins = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
var lg = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: lg.topAnchor),
containerView.leadingAnchor.constraint(equalTo: lg.leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: lg.trailingAnchor),
containerView.bottomAnchor.constraint(equalTo: lg.bottomAnchor)
])
containerView.addSubview(titleView)
containerView.layoutMargins = UIEdgeInsets(top: 15, left: 17, bottom: 15, right: 17)
lg = containerView.layoutMarginsGuide
playerView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(playerView)
NSLayoutConstraint.activate([
titleView.topAnchor.constraint(equalTo: lg.topAnchor),
titleView.leadingAnchor.constraint(equalTo: lg.leadingAnchor),
titleView.trailingAnchor.constraint(equalTo: lg.trailingAnchor),
// titleView.bottomAnchor.constraint(equalTo: lg.bottomAnchor),
playerView.topAnchor.constraint(equalTo: titleView.bottomAnchor),
playerView.leadingAnchor.constraint(equalTo: lg.leadingAnchor),
playerView.trailingAnchor.constraint(equalTo: lg.trailingAnchor),
playerView.bottomAnchor.constraint(equalTo: lg.bottomAnchor)
])
}
func configureFor(_ url: URL) {
let avPlayer = AVPlayer(url: url)
self.playerView.playerLayer.player = avPlayer
self.playerView.playerLayer.player?.play()
}
}
我有一个 UITableViewCell,它有一个 UILabel 和一个播放我需要实现的在线视频的 AVPlayer。问题是 Apple gives 的 AVPlayer 无法使用自动布局来正确显示视频。
我相信有一些方法可以实现混合约束和框架布局的 UIView。不过,我认为这个问题还有更多内容:视频未在初始化时加载这一事实似乎存在问题。
UITableViewCell
import UIKit
import AVKit
import AVFoundation
class PlayerView: UIView {
var player: AVPlayer? {
get {
return playerLayer.player
}
set {
playerLayer.videoGravity = .resizeAspect
playerLayer.player = newValue
}
}
var playerLayer: AVPlayerLayer {
return layer as! AVPlayerLayer
}
override static var layerClass: AnyClass {
return AVPlayerLayer.self
}
}
class CustomTableCell: UITableViewCell {
let titleView = UILabel()
let containerView = UIView()
let playerView = PlayerView()
required init?(coder aDecoder: NSCoder) {super.init(coder: aDecoder)}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
titleView.text = "Title"
titleView.translatesAutoresizingMaskIntoConstraints = false
contentView.clipsToBounds = true
containerView.clipsToBounds = true
contentView.addSubview(containerView)
containerView.translatesAutoresizingMaskIntoConstraints = false
//contentView.layoutMargins = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
var lg = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: lg.topAnchor),
containerView.leadingAnchor.constraint(equalTo: lg.leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: lg.trailingAnchor),
containerView.bottomAnchor.constraint(equalTo: lg.bottomAnchor)
])
containerView.addSubview(titleView)
containerView.layoutMargins = UIEdgeInsets(top: 15, left: 17, bottom: 15, right: 17)
lg = containerView.layoutMarginsGuide
playerView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(playerView)
NSLayoutConstraint.activate([
titleView.topAnchor.constraint(equalTo: lg.topAnchor),
titleView.leadingAnchor.constraint(equalTo: lg.leadingAnchor),
titleView.trailingAnchor.constraint(equalTo: lg.trailingAnchor),
// titleView.bottomAnchor.constraint(equalTo: lg.bottomAnchor),
playerView.topAnchor.constraint(equalTo: titleView.bottomAnchor),
playerView.leadingAnchor.constraint(equalTo: lg.leadingAnchor),
playerView.trailingAnchor.constraint(equalTo: lg.trailingAnchor),
playerView.bottomAnchor.constraint(equalTo: lg.bottomAnchor)
])
}
}
我在我的视图控制器中设置了一个 UITableView,但重点是 UITableViewDataSource cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReuseableCell(withIdentifier: "cell") as! CustomTableCell
let url = NSURL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let avPlayer = AVPlayer(url: url! as URL)
cell.playerView.playerLayer.player = avPlayer
return cell
}
但是我没有看到视频。显然,有一个类似 willLayoutSubviews 的函数或类似的函数,但是我如何打破标题视图的约束以使用 AVPlayerLayer 或类似的东西进行约束,以便我可以看到视频?
一般来说,我如何查看具有其他自动布局视图的自定义 UITableViewCell 中的 AVPlayer 或任何播放器?
编辑 1:@ugur 给出的答案是正确的,因为 AVPlayer 需要调整大小。使用 leadngAnchor 和 trailingAnchor 扩展视图无法解决问题,因为扩展的只是当前视图的大小。
使用 centerXAnchor、widthAnchor 和 heightAnchor 都可以。 centerXAnchor 将使您的玩家在视图中居中。
您需要告诉自动布局如何计算 playerView 高度。 您可以尝试以下选项:
为 playerView 设置恒定高度
playerView.heightAnchor.constraint(equalToConstant: 100)
或设置 playerView 的纵横比
playerView.heightAnchor.constraint(equalTo: playerView.widthAnchor, multiplier: 1.0)
所以自动布局将知道如何定位您的 playerView。否则 playerView 高度将为 0.
建议:
不要在 cellFor
中创建 AVPlayer
而是在 cell init 或 awakeFromNib
中创建(如果使用故事板)。所以 AVPlayer
也可以重复使用。只需使用新 url 或 playerItem
重置设置即可。您还可以根据您的情况使用播放或暂停。但是不要在每次调用 cellFor
时都重新创建 AVPlayer
。
所以你这里的主要问题是你无法在单元格中看到你的播放器。我已经修改了你们中的一些代码,然后它也会显示和播放视频。
注意:这是非常基本的代码,我相信您将能够根据您的要求进一步改进它。
您的 PlayerView
保持不变,变化如下。我添加了新方法 configureFor
,它只会获取 url 作为参数并设置播放器。
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableCell
let url = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
cell.configureFor(url!)
return cell
}
以下是您的单元格代码。
在这里,我已将 UI 设置代码移动到一个通用函数 commonInit()
,然后按如下方式使用它。
class CustomTableCell: UITableViewCell {
let titleView = UILabel()
let containerView = UIView()
let playerView = PlayerView()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
func commonInit() {
titleView.text = "Title"
titleView.translatesAutoresizingMaskIntoConstraints = false
contentView.clipsToBounds = true
containerView.clipsToBounds = true
containerView.backgroundColor = .orange
contentView.addSubview(containerView)
containerView.translatesAutoresizingMaskIntoConstraints = false
//contentView.layoutMargins = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
var lg = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: lg.topAnchor),
containerView.leadingAnchor.constraint(equalTo: lg.leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: lg.trailingAnchor),
containerView.bottomAnchor.constraint(equalTo: lg.bottomAnchor)
])
containerView.addSubview(titleView)
containerView.layoutMargins = UIEdgeInsets(top: 15, left: 17, bottom: 15, right: 17)
lg = containerView.layoutMarginsGuide
playerView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(playerView)
NSLayoutConstraint.activate([
titleView.topAnchor.constraint(equalTo: lg.topAnchor),
titleView.leadingAnchor.constraint(equalTo: lg.leadingAnchor),
titleView.trailingAnchor.constraint(equalTo: lg.trailingAnchor),
// titleView.bottomAnchor.constraint(equalTo: lg.bottomAnchor),
playerView.topAnchor.constraint(equalTo: titleView.bottomAnchor),
playerView.leadingAnchor.constraint(equalTo: lg.leadingAnchor),
playerView.trailingAnchor.constraint(equalTo: lg.trailingAnchor),
playerView.bottomAnchor.constraint(equalTo: lg.bottomAnchor)
])
}
func configureFor(_ url: URL) {
let avPlayer = AVPlayer(url: url)
self.playerView.playerLayer.player = avPlayer
self.playerView.playerLayer.player?.play()
}
}