我正在尝试设置可以播放视频的UITableView。之前的许多SO问题都使用了MPMoviePlayer(Playing Video into UITableView,Playing video in UItableView in SWIFT,Playing Video From UITableView),现在已经在iOS 9中弃用了。使用AVFoundation的少数人之一(我是什么&#39 ; m using),这是一个:Play video on UITableViewCell when it is completely visible,是我从中获取大部分代码的地方。这是我的代码,在cellForRowAtIndexPath中:
VideoCell *videoCell = (VideoCell *)[self.tableView dequeueReusableCellWithIdentifier:@"VideoCell"];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
NSURL *url = [[NSURL alloc] initWithString:urlString];
dispatch_async(queue, ^{
videoCell.item = [AVPlayerItem playerItemWithURL:url];
dispatch_sync(dispatch_get_main_queue(), ^{
videoCell.player = [[AVPlayer alloc] initWithPlayerItem:videoCell.item];
videoCell.player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:videoCell.player];
playerLayer.frame = CGRectMake(0, 0, videoCell.contentView.frame.size.width, videoCell.contentView.frame.size.height);
[videoCell.contentView.layer addSublayer:playerLayer];
playerLayer.videoGravity = AVLayerVideoGravityResize;
[videoCell.player play];
});
});
return videoCell;
根据我的理解,我需要在播放之前异步下载视频。我以前异步下载过图像,并且下载了#34;其中一部分总是涉及转换NSURL - > NSData - > UIImage的。然后当你拥有UIImage时,你已准备好在单元格中显示它,所以你将主队列调高并调度dispas_async并执行cell.imageView.image = yourImage;在主队列上。
在这里,我有一个NSURL,但是我不太明白这里应该在主队列上执行哪些步骤,哪些步骤应该在主线程之外。我尝试了上述SO问题中的建议,但到目前为止它还没有工作。表格单元格只是加载,视频永远不会播放。我只看到第一帧。有时它的前1秒会播放,但之后它会缓冲并且不会被激活。我正在使用目前只有1个对象的表视图,所以只有1个视频可以播放,但它仍然没有播放。
我做错了什么,有人可以帮我解释一下主要线程上需要哪些部分以及哪些部分关闭?我在顶部提到的帖子中的响应者说,有很多关于此的教程,"但在扫描谷歌时,我没有看到任何。术语"异步"和" iOS"几乎总是得到关于图像下载的搜索结果,而不是视频。但是如果存在任何教程,那么很高兴看到它。
感谢
答案 0 :(得分:2)
以下是我用于在tableview单元格中播放视频的方法。我不确定这是最好的方法,无论如何它可以帮助你:)。
首先,我创建了一个自定义单元格。 在setter方法中,我调用了一个设置AVPlayer的方法。
- (void)setUpAVPlayer
{
@try {
self.videoPlayer = [[AVPlayer alloc]initWithPlayerItem:self.videoPlayerItem];
}
@catch (NSException *exception) {
NSLog(@"Exception : %@",exception.description);
[self.videoPlayer replaceCurrentItemWithPlayerItem:self.videoPlayerItem];
}
// Setting player properties.
playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.videoPlayer];
playerLayer.videoGravity = AVLayerVideoGravityResize;
[self.previewImageView.layer addSublayer:playerLayer];
self.previewImageView.clipsToBounds = YES;
playerLayer.hidden = YES;
[playerLayer addObserver:self forKeyPath:@"readyForDisplay" options:0 context:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pauseAllRunningPlayers) name:@"pause" object:nil];
// AVPlayer KVO
[self.videoPlayer addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:kRateDidChangeKVO];
[self.videoPlayer addObserver:self forKeyPath:@"currentItem.status" options:NSKeyValueObservingOptionNew context:kStatusDidChangeKVO];
[self.videoPlayer addObserver:self forKeyPath:@"currentItem.duration" options:NSKeyValueObservingOptionNew context:kDurationDidChangeKVO];
[self.videoPlayer addObserver:self forKeyPath:@"currentItem.loadedTimeRanges" options:NSKeyValueObservingOptionNew context:kTimeRangesKVO];
[self.videoPlayer addObserver:self forKeyPath:@"currentItem.playbackBufferFull" options:NSKeyValueObservingOptionNew context:kBufferFullKVO];
[self.videoPlayer addObserver:self forKeyPath:@"currentItem.playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:kBufferEmptyKVO];
[self.videoPlayer addObserver:self forKeyPath:@"currentItem.error" options:NSKeyValueObservingOptionNew context:kDidFailKVO];
[videoLoadingIndicator stopAnimating];
}
我发现在你的代码中你已经编写了像这个[videoCell.player play];
只有当玩家状态变为' AVPlayerStatusReadyToPlay'时才应调用播放功能。这就是我使用KVO的原因。
希望这可以帮助你:)
答案 1 :(得分:0)
在这里,我准备了一个演示,其中根据您的要求,一旦您的单元格可见,播放器将开始播放视频,您可以参考下图来设计故事板。
//
// ViewController.swift
// RTLDemo
//
// Created by iOS Test User on 06/01/18.
// Copyright © 2018 iOS Test User. All rights reserved.
//
import UIKit
import AVKit
public struct VideoName {
static let video1 = "video1"
static let video2 = "video2"
}
public struct MediaType {
static let video = 1
static let image = 2
}
class ViewController: UIViewController {
@IBOutlet weak var collView: UICollectionView!
var timer: Timer?
var isVideoPlaying: Bool = false
// ------------------------------------------------------------------------------------------
// MARK: -
// MARK: - Memory management method
// ------------------------------------------------------------------------------------------
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// ------------------------------------------------------------------------------------------
// MARK:
// MARK: - Custom Methods
// ------------------------------------------------------------------------------------------
func initialSetup() {
if self.timer == nil {
self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(checkForTheVisibleVideo), userInfo: nil, repeats: true)
}
timer?.fire()
}
// ------------------------------------------------------------------------------------------
@objc func checkForTheVisibleVideo() {
if !isVideoPlaying {
let visibleCell = self.collView.indexPathsForVisibleItems
if visibleCell.count > 0 {
for indexPath in visibleCell {
if self.isVideoPlaying {
break
}
if let cell = self.collView.cellForItem(at: indexPath) as? CustomCell,cell.mediaType == MediaType.video {
if cell.player == nil{
cell.player = AVPlayer(url: URL.init(fileURLWithPath: Bundle.main.path(forResource: VideoName.video2, ofType: "mp4")!))
cell.playerLayer = AVPlayerLayer.init(player: cell.player)
cell.playerLayer?.frame = cell.imgView.frame
cell.imgView.layer.addSublayer(cell.playerLayer!)
NotificationCenter.default.addObserver(self, selector: #selector(videoDidFinishPlaying), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: cell.player?.currentItem)
cell.player?.addPeriodicTimeObserver(forInterval: CMTime.init(seconds: 1, preferredTimescale: 1), queue: .main, using: { (time) in
if cell.player?.currentItem?.status == .readyToPlay {
let timeDuration : Float64 = CMTimeGetSeconds((cell.player?.currentItem?.asset.duration)!)
cell.lblDuration.text = self.getDurationFromTime(time: timeDuration)
let currentTime : Float64 = CMTimeGetSeconds((cell.player?.currentTime())!)
cell.lblStart.text = self.getDurationFromTime(time: currentTime)
cell.slider.maximumValue = Float(timeDuration.rounded())
cell.slider.value = Float(currentTime.rounded())
}
})
}
cell.player?.play()
cell.btnPlay.setImage(#imageLiteral(resourceName: "pause_video"), for: .normal)
self.isVideoPlaying = true
}
}
}
}
}
// ------------------------------------------------------------------------------------------
@objc func videoDidFinishPlaying() {
self.isVideoPlaying = false
let visibleItems: Array = self.collView.indexPathsForVisibleItems
if visibleItems.count > 0 {
for currentCell in visibleItems {
guard let cell = self.collView.cellForItem(at: currentCell) as? CustomCell else {
return
}
if cell.player != nil {
cell.player?.seek(to: kCMTimeZero)
cell.player?.play()
}
}
}
}
// ------------------------------------------------------------------------------------------
@objc func onSliderValChanged(slider: UISlider, event: UIEvent) {
if let touchEvent = event.allTouches?.first {
guard let cell = self.collView.cellForItem(at: IndexPath.init(item: slider.tag, section: 0)) as? CustomCell else {
return
}
switch touchEvent.phase {
case .began:
cell.player?.pause()
case .moved:
cell.player?.seek(to: CMTimeMake(Int64(slider.value), 1))
case .ended:
cell.player?.seek(to: CMTimeMake(Int64(slider.value), 1))
cell.player?.play()
default:
break
}
}
}
// ------------------------------------------------------------------------------------------
func getDurationFromTime(time: Float64)-> String {
let date : Date = Date(timeIntervalSince1970: time)
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone.init(identifier: "UTC")
dateFormatter.dateFormat = time < 3600 ? "mm:ss" : "HH:mm:ss"
return dateFormatter.string(from: date)
}
// ------------------------------------------------------------------------------------------
@IBAction func btnPlayTapped(_ sender: UIButton) {
let indexPath = IndexPath.init(item: sender.tag, section: 0)
guard let cell = self.collView.cellForItem(at: indexPath) as? CustomCell else {
return
}
if isVideoPlaying {
self.isVideoPlaying = false
cell.btnPlay.setImage(#imageLiteral(resourceName: "play_video"), for: .normal)
cell.player?.pause()
}else{
if cell.player == nil {
cell.player = AVPlayer(url: URL.init(fileURLWithPath: Bundle.main.path(forResource: VideoName.video2, ofType: "mp4")!))
cell.playerLayer = AVPlayerLayer(player: cell.player!)
cell.playerLayer?.frame = CGRect.init(x: 0, y: 0, width: UIScreen.main.bounds.size.width - 50, height: (UIScreen.main.bounds.size.height - 64) * 0.3)
cell.imgView.layer.addSublayer(cell.playerLayer!)
NotificationCenter.default.addObserver(self, selector: #selector(videoDidFinishPlaying), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: cell.player?.currentItem)
}
cell.btnPlay.setImage(#imageLiteral(resourceName: "pause_video"), for: .normal)
cell.player?.play()
self.isVideoPlaying = true
}
}
// ------------------------------------------------------------------------------------------
// MARK: -
// MARK: - View life cycle methods
// ------------------------------------------------------------------------------------------
override func viewDidLoad() {
super.viewDidLoad()
self.initialSetup()
}
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate,UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
// ------------------------------------------------------------------------------------------
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = self.collView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
if indexPath.row % 2 == 0 {
cell.mediaType = MediaType.image
cell.btnPlay.isHidden = true
cell.lblDuration.isHidden = true
cell.lblStart.isHidden = true
cell.slider.isHidden = true
cell.imgView.isHidden = false
}else{
cell.mediaType = MediaType.video
cell.btnPlay.isHidden = false
cell.lblDuration.isHidden = false
cell.lblStart.isHidden = false
cell.slider.isHidden = false
cell.imgView.isHidden = false
}
cell.btnPlay.tag = indexPath.row
cell.slider.tag = indexPath.row
cell.btnPlay.addTarget(self, action: #selector(btnPlayTapped(_:)), for: .touchUpInside)
cell.slider.addTarget(self, action: #selector(self.onSliderValChanged(slider:event:)), for: .valueChanged)
return cell
}
// ------------------------------------------------------------------------------------------
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize.init(width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height / 3)
}
// ------------------------------------------------------------------------------------------
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
guard let cellToHide = cell as? CustomCell else {
return
}
if cellToHide.player != nil {
cellToHide.player?.pause()
cellToHide.playerLayer?.removeFromSuperlayer()
cellToHide.player = nil
cellToHide.btnPlay.setImage(#imageLiteral(resourceName: "play_video"), for: .normal)
self.isVideoPlaying = false
}
}
}
// Custom Cell Class
class CustomCell: UICollectionViewCell {
@IBOutlet weak var btnPlay: UIButton!
@IBOutlet weak var lblDuration: UILabel!
@IBOutlet weak var imgView: UIImageView!
@IBOutlet weak var viewBack: UIView!
@IBOutlet weak var lblStart: UILabel!
@IBOutlet weak var slider: UISlider!
var player: AVPlayer?
var playerLayer: AVPlayerLayer?
var mediaType: Int!
}
故事板参考图像,其中我创建了具有集合视图单元格的简单自定义视频播放器:
答案 2 :(得分:0)
很快,您可以使用下面的库代码在tableView单元格中播放视频: