2015-11-13 3 views
4

Каков наилучший способ для встраивания видео в UITableViewCell? Я пытаюсь создать что-то вроде Vine/Instagram.Вложение видео в ячейку tableview

Я могу обрабатывать асинхронную загрузку изображений с помощью SD_WebImage ... но, к сожалению, они не поддерживают видео. Я также пытался встраиваться в MPMoviePlayer, но он просто выглядит как черный экран. Это то, что я пробовал:

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 

} 

ответ

5

MPMoviePlayerController осуждался в прошивкой 9, вы должны использовать вместо AVPlayer как следующим образом:

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 
} 

Вам необходимо включить две рамки:

import AVKit 
import AVFoundation 

Чтобы получить видимую ячейку/строки в UITableView, вы можете использовать два свойства только для чтения:

Я надеюсь, что это поможет вам.

+0

это работает, но это действительно Glitchy, когда я пытаюсь делать больше, чем на одну клетку ... и особенно, когда я пытаюсь прокрутки. Любые идеи о том, как это исправить? –

+0

Вам нужно обработать в 'cellForRowAtIndexPath' то, что вам нужно показать. Что именно вам нужно? –

+0

Мне нужно что-то вроде виноградной лозы, где, когда пользователь прокручивает камеру, видео начинает играть. Я думал, что изначально я мог просто показать исходный кадр видео (как изображение), а затем, когда ячейка станет видимой, начните воспроизведение видео. У меня возникли проблемы с выяснением, как это сделать. Есть ли функция tableView, которая позволяет мне проверять visibleRow? –

13

Я проверил демо только видео,

Вот как вы можете достичь it- Создание пользовательского класса Cell, чтобы держать вид видеоплеер, и ручки воспроизведения и паузы способы AVPlayer здесь сам.

Это мой пользовательский класс Cell -

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) 
    } 

} 

Затем приходит ваш контроллер - Не забудьте, чтобы импортировать 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 
    } 

Затем укажите свой адрес в cellForRow делегата

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 
    } 

Вся часть видимых ячеек здесь я использовал расчет пересечения всех видимых ячеек здесь,

Найти visible IndexPath, использовать это для извлечения ячейки пользовательского типа tablecell. Это также может быть достигнуто с помощью visibleCells, но я избегал этого, так как вы можете иметь несколько типов ячеек, имеющих изображение, текст или другие материалы.

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])!) 
         } 

        } 
       } 
      } 
     } 
    } 

Используйте эти методы для воспроизведения.

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

Если интересно, you can check the demo here

+2

Я ЛЮБЛЮ ТЕБЯ! –

+0

Еще одна вещь, у меня есть 3 видео, которые показывают в то же время .. иногда они просто не запускаются. –

+0

И знаете ли вы, как я могу кэшировать видео? –