带有其他视图自动布局的UITableViewCell中的AVPlayer

5
我有一个UITableViewCell,其中包含一个UILabel和一个AVPlayer,用于播放在线视频。问题在于,Apple提供的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将使您的播放器在视图中心。

你能分享一些它是如何显示的屏幕截图吗? - user12688502
@Niraj_iOS 我只看到文字,视频没有出现。实际上,即使它出现了,如果用户重新调整设备方向,那么视频也会变成一场灾难。 - Yoomama
你可以在Storyboard中放置UIView,并在该视图上编程添加播放器。 - user12688502
@Yoomama 请看一下以下答案,希望能解决你的问题。 - Harshal Bhavsar
@Yoomama,你有没有查看下面的答案? - Harshal Bhavsar
2个回答

1

你需要告诉自动布局如何计算播放器视图的高度。 你可以尝试以下选项:

  1. set a constant height to playerView

    playerView.heightAnchor.constraint(equalToConstant: 100)
    
  2. or set a aspect ratio to playerView

    playerView.heightAnchor.constraint(equalTo: playerView.widthAnchor, multiplier: 1.0)
    

如果没有为其提供位置信息,自动布局将不知道如何定位您的playerView。否则,playerView的高度将为0。

建议:

不要在cellFor中创建AVPlayer,而是在单元格初始化或awakeFromNib(如果使用storyboard)中进行。这样AVPlayer也将是可重用的。只需使用新的url或playerItem重置设置即可。根据您的情况,还可以使用播放或暂停。但是,不要每次调用cellFor时都重新创建AVPlayer


如果我同时设置高度和宽度锚定(以及顶部和底部锚定(不知道???)),视频会变形吗? 此外,我将其放在cellFor中,因为我正在使用此链接服务器,并需要某种索引来将数据数组与单元格链接。否则我该怎么做? - Yoomama
你介意给一个例子,说明如何在单元格的init中实例化,而不是在cellFor中吗?我对AVKit的经验相当有限。 - Yoomama

0

你的主要问题在于无法在单元格中看到你的播放器。我修改了你的一些代码,现在它可以显示和播放视频了。

注意:这是非常基础的代码,我相信你可以根据自己的需求进一步改进它。

你的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()
    }
}

抱歉,它不起作用。我所担心的是leadingAnchor和trailingAnchor。请注意,这个UIView不是普通的UIView。您必须增加AVPlayer的大小,这需要高度和宽度锚点。 - Yoomama
好的,因为我在本地尝试过,它显示了播放器并且视频也在播放。 - Harshal Bhavsar
抱歉,模拟器上我只看到了iOS 13 iPhone 11 Pro Max上的橙色单元格。请检查@ugur给出的答案。不想无礼,但您的init并没有起到帮助的作用。 - Yoomama
将init移动到自定义函数中不会扩大PlayerView。您需要这些锚点才能显示视频。在初始化后适配视频对于任何视图都不起作用,除非重新实例化该视图。 - Yoomama
变化也在于您如何配置单元格,请参阅索引路径处的行单元格。 - Harshal Bhavsar
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接