将视频嵌入UITableViewCell的最佳方法是什么?我正在尝试构建类似Vine / Instagram的东西.
我能够很好地处理异步图像加载SD_WebImage ..但不幸的是它们不支持视频.我也尝试使用MPMoviePlayer进行嵌入,但它只是显示为黑屏.这是我试过的:
override func viewDidLoad() { super.viewDidLoad() tableView.frame = CGRectMake(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 }
我只测试了一个视频演示,
以下是如何实现它 –
创建一个自定义Cell类来保存视频播放器视图,并在这里处理avplayer的播放和暂停方法.
这是我的自定义单元类 –
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,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,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委托中提供您的URL
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 }
这里管理了可见细胞的所有部分,我在这里使用了所有可见细胞的交集计算,
找到可见的IndexPath,使用它来获取自定义表格单元格类型的单元格.
这也可以通过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() } } }