在 tableview 单元格中嵌入视频
将视频嵌入 UITableViewCell 的最佳方法是什么?我正在尝试构建类似于 Vine/Instagram 的东西.
What's the best way for embedding a video into a UITableViewCell? I'm trying to build something sorta like Vine/Instagram.
我能够使用 SD_WebImage 很好地处理异步图像加载……但不幸的是,它们不支持视频.我也尝试嵌入 MPMoviePlayer,但它只是显示为黑屏.这是我试过的:
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,
这是您实现它的方法-创建一个自定义 Cell 类来保存视频播放器视图,并在此处自行处理 avplayer 的播放和暂停方法.
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)
}
}
然后是你的控制器 -不要忘记导入 AVFoundation
框架
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
}
然后在 cellForRow 委托中提供您的 URL
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,
找到visible IndexPath
,使用它来获取自定义tablecell
类型的单元格.这也可以通过 visibleCells
来实现,但我已经避免了,因为您可以拥有多种类型的包含图像、文本或其他内容的单元格.
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()
}
}
}
如果有兴趣,你可以在这里查看演示