将视频嵌入UITableViewCell的最佳方法是什么?我试图建立像Vine / Instagram这样的东西。
我能够很好地处理异步图像加载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
}
答案 0 :(得分:16)
我只测试了一个视频演示,
您可以通过以下方式实现目标 - 创建一个自定义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, 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
框架
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()
}
}
}
答案 1 :(得分:6)
MPMoviePlayerController
在iOS 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
中的可见单元格/行,您可以使用两个只读属性:
我希望这对你有所帮助。