将视频嵌入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()
- }
- }
- }