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 really well with SD_WebImage..but unfortunately they don't support video. I also tried embedding with an MPMoviePlayer but it just appears as a black screen. This is what I tried:
override func viewDidLoad() {
    super.viewDidLoad()
    tableView.frame         =   CGRectMake(0, 0, view.bounds.width, view.bounds.height);
    tableView.delegate      =   self
    tableView.dataSource    =   self
    tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
    self.view.addSubview(tableView)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    var cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
    var moviePlayer : MPMoviePlayerController?
    let url = NSURL (string: "http://jplayer.org/video/m4v/Big_Buck_Bunny_Trailer.m4v")
    moviePlayer = MPMoviePlayerController(contentURL: url)
    if let player = moviePlayer {
        player.view.frame = CGRectMake(0, 100, view.bounds.size.width, 180)
        player.prepareToPlay()
        player.controlStyle = .None
        player.repeatMode = .One
        player.scalingMode = .AspectFit
        cell.addSubview(player.view)
    }
    return cell
}
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
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:
visibleCellsindexPathsForVisibleRowsI hope this help you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With