在UITableViewCell中添加AVPlayer或MPMoviePlayerController的最佳位置是什么?

时间:2017-06-07 05:16:02

标签: ios objective-c uitableview avplayer mpmovieplayercontroller

当用户点击单元格中的播放按钮时,我尝试在UITableViewCell中播放视频。

2 个答案:

答案 0 :(得分:3)

场景1:

如果您愿意在每个单元格中自动播放Facebook或Instagram等视频,请考虑在AVPlayer中添加cellForRowAtIndexPath

AVPlayer一旦创建,将不允许您更改正在播放的网址(AVURLAsset网址)。因此,如果您正在使用AVPlayer并向玩家提供自己的观点,我恐怕每次在AVPlayer中创建cellForRowAtIndexPath实例都是唯一的解决方案。

另一方面,如果您打算使用AVPlayerViewController,则可以在单元格AVPlayer中创建viewController awakeFromNib个实例,这样您就只有一个每个单元格的玩家实例,cellForRowAtIndexPath每次都可以传递不同的网址。您可以使用cell的prepareForReuse方法暂停播放器并重置所有播放器属性。

场景2:

如果您计划在用户点按单元格中的按钮后播放,请考虑在按钮上点击创建AVPlayer / AVPlayerViewController并播放视频。使用prepareForReuse重置播放器的属性,以确保在滚动和重复使用单元格时不会播放错误的视频。

修改

OP要求提供一些代码片段,提供骨架代码。这不是复制粘贴代码,想法只是想知道如何在单元格中使用AVPlayerViewController代码可能有编译器问题。

//创建自定义tableViewCell子类

class ImageTableViewCell: UITableViewCell {
    @IBOutlet weak var carsImageView: UIImageView!
    var playerController : AVPlayerViewController? = nil
    var passedURL : URL! = nil

    override func awakeFromNib() {
        super.awakeFromNib()
        playerController = AVPlayerViewController()
        // Initialization code
    }

    func configCell(with url : URL) {
        //something like this
        self.passedURL = url
        //show video thumbnail with play button on it.
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        // Configure the view for the selected state
    }

    @IBAction func playOrPauseVideo(_ sender: UIButton) {
        let player = AVPlayer(url: url)
        playerController.player = player
        playerController?.showsPlaybackControls = true
        playerController.player.play()
        //add playerController view as subview to cell
    }

    override func prepareForReuse() {
        //this way once user scrolls player will pause
        self.playerController.player?.pause()
        self.playerController.player = nil
    }
}

上面的代码将PlayerController设置为prepareForReuse()中的nil,因此当用户滚动tableView并且单元格退出tableView Frame并重新使用时,播放器将暂停不会保留状态。如果您只是想在用户滚动回同一个单元格时暂停和重放,则必须将玩家保存在单元格外的某个位置,并找到将玩家实例映射到单元格的方法。

最后在cellForRowAtIndexPath来电,

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell : ImageTableViewCell = tableView.dequeueReusableCell(withIdentifier: "imageCell") as! ImageTableViewCell
    cell.configCell(with: URL(string: "https://google.com")!)
    ....
}

编辑2:

由于OP完全提出了在任何时间点只播放一个视频并暂停其他播放视频点击播放按钮的问题,我之前的回答将不再适用。

早期回答允许用户播放多个视频。

以下是修改后的答案,以使其有效。

修改您的自定义单元格类,如下所示。

protocol VideoPlayingCellProtocol : NSObjectProtocol {
    func playVideoForCell(with indexPath : IndexPath)
}

class ImageTableViewCell: UITableViewCell {
    @IBOutlet weak var carsImageView: UIImageView!
    var playerController : AVPlayerViewController? = nil
    var passedURL : URL! = nil
    var indexPath : IndexPath! = nil
    var delegate : VideoPlayingCellProtocol = nil

    override func awakeFromNib() {
        super.awakeFromNib()
        playerController = AVPlayerViewController()
        // Initialization code
    }

    func configCell(with url : URL,shouldPlay : Bool) {
        //something like this
        self.passedURL = url
        if shouldPlay == true {
            let player = AVPlayer(url: url)
            if self.playerController == nil {
                playerController = AVPlayerViewController()
            }
            playerController.player = player
            playerController?.showsPlaybackControls = true
            playerController.player.play()
        }
        else {
            if self.playerController != nil {
                self.playerController.player?.pause()
                self.playerController.player = nil
            }
            //show video thumbnail with play button on it.
        }
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

    @IBAction func playOrPauseVideo(_ sender: UIButton) {
        self.delegate.playVideoForCell(self.indexPath)
        //add playerController view as subview to cell
    }

    override func prepareForReuse() {
        //this way once user scrolls player will pause
        self.playerController.player?.pause()
        self.playerController.player = nil
    }
}

在TableView VC中创建一个名为

的属性
var currentlyPlayingIndexPath : IndexPath? = nil

让TableView VC确认VideoPlayingCellProtocol

extension ViewController : VideoPlayingCellProtocol {
    func playVideoForCell(with indexPath: IndexPath) {
    self.currentlyPlayingIndexPath = indexPath
    //reload tableView
    self.tableView.reloadRows(at: self.tableView.indexPathsForVisibleRows!, with: .none)
   }
}

最后将cellForRowAtIndexPath修改为

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell : ImageTableViewCell = tableView.dequeueReusableCell(withIdentifier: "imageCell") as! ImageTableViewCell
        //delegate setting here which u missed
        cell.delegate = self
        //let the cell know its indexPath
        cell.indexPath = indexPath

            cell.configCell(with: URL(string: "https://google.com")!, shouldPlay: self.currentlyPlayingIndexPath == indexPath)
        ....
}

这就是你所需要的一切。

答案 1 :(得分:3)

我有与您相似的要求,并在AVPlayer中创建了全局AVPlayerLayerUITableViewController变量。然后,当一个人选择了细胞时,我将它们连接到细胞上。从我所看到的情况来看,我的滚动很流畅,内存使用率很低,并且它还强制一次只播放一个视频。

第一步是在UITableViewController

中创建变量
lazy var avPlayer : AVPlayer = {
    let player = AVPlayer()
    return player
}()

lazy var avPlayerLayer : AVPlayerLayer = {
    let layer = AVPlayerLayer(player: self.avPlayer)
    return layer
}()

然后我在UITableViewController中创建了两个函数来处理链接并从单元格中删除AVPlayerLayer

private func removePlayerFromCell()
{
    if (self.avPlayerLayer.superlayer != nil)
    {
        self.avPlayerLayer.removeFromSuperlayer()
        self.avPlayer.pause()
        self.avPlayer.replaceCurrentItem(with: nil)
    }
}

private func attachPlayerToCell(indexPath : IndexPath)
{
    if let cell = tableView.cellForRow(at: indexPath)
    {
        self.removePlayerFromCell()

        //create url
       let url = ... 

        //create player item
        let playerItem = AVPlayerItem(url: url)
        self.avPlayer.replaceCurrentItem(with: playerItem)

        self.avPlayerLayer.frame = cell.contentView.frame
        cell.contentView.layer.addSublayer(self.avPlayerLayer)

        self.avPlayer.play()
    }
}

然后,您可以在attachPlayerToCell()中致电didSelectRowAt()

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
    attachPlayerToCell(indexPath: indexPath)
}

最后,要确保视频在滚动期间不播放,请使用表格UIScrollViewDelegate停止播放:

override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    self.removePlayerFromCell()
}

希望这有帮助并且很清楚。