Embedding videos in a tableview cell

前端 未结 2 967
滥情空心
滥情空心 2020-12-08 12:06

What\'s the best way for embedding a video into a UITableViewCell? I\'m trying to build something sorta like Vine/Instagram.

I\'m able to handle asynch image loading

相关标签:
2条回答
  • 2020-12-08 13:09

    MPMoviePlayerController was deprecated in iOS 9, you should use instead AVPlayer like in the following way:

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
    
        let videoURL = NSURL(string: "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
        let player = AVPlayer(URL: videoURL!)
    
        let playerLayer = AVPlayerLayer(player: player)
        playerLayer.frame = cell.bounds
    
        cell.layer.addSublayer(playerLayer)
        player.play()
    
        return cell
    }
    

    You need to include the two frameworks :

    import AVKit
    import AVFoundation
    

    To get the visibles cell/rows in the UITableView you can use the two read-only properties:

    • visibleCells
    • indexPathsForVisibleRows

    I hope this help you.

    0 讨论(0)
  • 2020-12-08 13:13

    I have tested a demo for videos only,

    Here is how you can achieve it- Create a custom Cell class to hold the video player view, and the handle the play and pause methods for the avplayer here itself.

    This is my Custom Cell class -

    import UIKit
    import AVFoundation
    
    class VideoCellTableViewCell: UITableViewCell {
    
        // I have put the avplayer layer on this view
        @IBOutlet weak var videoPlayerSuperView: UIView!
        var avPlayer: AVPlayer?
        var avPlayerLayer: AVPlayerLayer?
        var paused: Bool = false
    
        //This will be called everytime a new value is set on the videoplayer item
        var videoPlayerItem: AVPlayerItem? = nil {
            didSet {
                /*
                 If needed, configure player item here before associating it with a player.
                 (example: adding outputs, setting text style rules, selecting media options)
                 */
                avPlayer?.replaceCurrentItem(with: self.videoPlayerItem)
            }
        }
    
        override func awakeFromNib() {
            super.awakeFromNib()
            //Setup you avplayer while the cell is created
            self.setupMoviePlayer()
        }
    
        func setupMoviePlayer(){
            self.avPlayer = AVPlayer.init(playerItem: self.videoPlayerItem)
            avPlayerLayer = AVPlayerLayer(player: avPlayer)
            avPlayerLayer?.videoGravity = AVLayerVideoGravityResizeAspect
            avPlayer?.volume = 3
            avPlayer?.actionAtItemEnd = .none
    
            //        You need to have different variations
            //        according to the device so as the avplayer fits well
            if UIScreen.main.bounds.width == 375 {
                let widthRequired = self.frame.size.width - 20
                avPlayerLayer?.frame = CGRect.init(x: 0, y: 0, width: widthRequired, height: widthRequired/1.78)
            }else if UIScreen.main.bounds.width == 320 {
                avPlayerLayer?.frame = CGRect.init(x: 0, y: 0, width: (self.frame.size.height - 120) * 1.78, height: self.frame.size.height - 120)
            }else{
                let widthRequired = self.frame.size.width
                avPlayerLayer?.frame = CGRect.init(x: 0, y: 0, width: widthRequired, height: widthRequired/1.78)
            }
            self.backgroundColor = .clear
            self.videoPlayerSuperView.layer.insertSublayer(avPlayerLayer!, at: 0)
    
            // This notification is fired when the video ends, you can handle it in the method.
            NotificationCenter.default.addObserver(self,
                                                   selector: #selector(self.playerItemDidReachEnd(notification:)),
                                                   name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
                                                   object: avPlayer?.currentItem)
        }
    
        func stopPlayback(){
            self.avPlayer?.pause()
        }
    
        func startPlayback(){
            self.avPlayer?.play()
        }
    
        // A notification is fired and seeker is sent to the beginning to loop the video again
        func playerItemDidReachEnd(notification: Notification) {
            let p: AVPlayerItem = notification.object as! AVPlayerItem
            p.seek(to: kCMTimeZero)
        }
    
    }
    

    Then comes your controller - Dont forget to import the AVFoundation Framework

    import UIKit
    import AVFoundation
    
    class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
     // The current VisibleIndexPath, 
     //it can be an array, but for now,
     //i am targetting one cell only
     //var visibleIP : IndexPath? 
    
        var aboutToBecomeInvisibleCell = -1
        var avPlayerLayer: AVPlayerLayer!
        var videoURLs = Array<URL>()
        var firstLoad = true
    
        @IBOutlet weak var feedTableView: UITableView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            feedTableView.delegate = self
            feedTableView.dataSource = self
       //Your model to hold the videos in the video URL
            for i in 0..<2{
                let url = Bundle.main.url(forResource:"\(i+1)", withExtension: "mp4")
                videoURLs.append(url!)
            }
        // initialized to first indexpath
            visibleIP = IndexPath.init(row: 0, section: 0)
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return 5
        }
    
        func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            return 290
        }
    
        func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
            return 0
        }
    

    Then provide your URL in the cellForRow delegate

        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      //Thats it, just provide the URL from here, it will change with didSet Method in your custom cell class
            let cell = self.feedTableView.dequeueReusableCell(withIdentifier: "videoCell") as! VideoCellTableViewCell
            cell.videoPlayerItem = AVPlayerItem.init(url: videoURLs[indexPath.row % 2])
            return cell
        }
    

    All the part for visible cells is managed here, I have used the calculation of the intersection all the visible cells here,

    Find the visible IndexPath, use that to fetch a cell of the custom tablecell type. This can also be achieved with visibleCells but, i have avoided that, as you can have multiple type of cells having image, text or other stuff.

        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            let indexPaths = self.feedTableView.indexPathsForVisibleRows
            var cells = [Any]()
            for ip in indexPaths!{
                if let videoCell = self.feedTableView.cellForRow(at: ip) as? VideoCellTableViewCell{
                    cells.append(videoCell)
                }
            }
            let cellCount = cells.count
            if cellCount == 0 {return}
            if cellCount == 1{
                if visibleIP != indexPaths?[0]{
                    visibleIP = indexPaths?[0]
                }
                if let videoCell = cells.last! as? VideoCellTableViewCell{
                    self.playVideoOnTheCell(cell: videoCell, indexPath: (indexPaths?.last)!)
                }
            }
            if cellCount >= 2 {
                for i in 0..<cellCount{
                    let cellRect = self.feedTableView.rectForRow(at: (indexPaths?[i])!)
                    let intersect = cellRect.intersection(self.feedTableView.bounds)
    //                curerntHeight is the height of the cell that
    //                is visible
                    let currentHeight = intersect.height
                    print("\n \(currentHeight)")
                    let cellHeight = (cells[i] as AnyObject).frame.size.height
    //                0.95 here denotes how much you want the cell to display
    //                for it to mark itself as visible,
    //                .95 denotes 95 percent,
    //                you can change the values accordingly
                    if currentHeight > (cellHeight * 0.95){
                        if visibleIP != indexPaths?[i]{
                            visibleIP = indexPaths?[i]
                            print ("visible = \(indexPaths?[i])")
                            if let videoCell = cells[i] as? VideoCellTableViewCell{
                                self.playVideoOnTheCell(cell: videoCell, indexPath: (indexPaths?[i])!)
                            }
                        }
                    }
                    else{
                        if aboutToBecomeInvisibleCell != indexPaths?[i].row{
                            aboutToBecomeInvisibleCell = (indexPaths?[i].row)!
                            if let videoCell = cells[i] as? VideoCellTableViewCell{
                                self.stopPlayBack(cell: videoCell, indexPath: (indexPaths?[i])!)
                            }
    
                        }
                    }
                }
            }
        }
    

    Use these methods to handle the playback.

        func playVideoOnTheCell(cell : VideoCellTableViewCell, indexPath : IndexPath){
            cell.startPlayback()
        }
    
        func stopPlayBack(cell : VideoCellTableViewCell, indexPath : IndexPath){
            cell.stopPlayback()
        }
    
        func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
            print("end = \(indexPath)")
            if let videoCell = cell as? VideoCellTableViewCell {
                videoCell.stopPlayback()
            }
        }
    }
    

    If interested, you can check the demo here

    0 讨论(0)
提交回复
热议问题