gpt4 book ai didi

ios - 对自定义 AVPlayer 的错误引用

转载 作者:行者123 更新时间:2023-11-29 13:51:59 25 4
gpt4 key购买 nike

我有一个包含多个单元格 (FeedCell) 的 UICollectionView。这些单元格还可以包含一个 UICollectionView,它也可以有多个单元格,最多五个 (MediaSliderCell)。基本上,设置与 Instagram 相同:您有一个帖子,一个帖子可以有多个图像或视频。

我现在面临的问题是,有时错误的视频会显示在错误的单元格中。当视频还没有播放时,我使用 imageView 来显示一个占位符,当视频开始播放时这个占位符是隐藏的(点击播放按钮)。

当我不播放视频时,我认为一切正常,但是当我播放视频时,问题就出现了。单元格切换,这意味着 MediaSliderCell indexPath.item 5 的视频显示在 MediaSLiderCell indexPath.item 2 中,例如。

起初,我认为问题出在细胞中,细胞没有得到很好的重复使用,但这也意味着照片可能会被调换,但这种情况从未发生过。所以我觉得问题出在我的 AVPlayer 中,它使用了错误的引用或错误的 URL。让我演示一下我的代码:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mediaSliderCell", for: indexPath) as! MediaSliderCell
cell.layer.shouldRasterize = true
cell.layer.rasterizationScale = UIScreen.main.scale
if(HomeControllerID.userFeed.userFeed[feedCellID!].userMedia!.count > 0) {
if(HomeControllerID.userFeed.userFeed[feedCellID!].userMedia![indexPath.item].mediaType == .photo) {
let mediaURL = URL(string: "https://myfileserver.com/media/\(HomeControllerID.userFeed.userFeed[feedCellID!].feedID!)/\(HomeControllerID.userFeed.userFeed[feedCellID!].userMedia![indexPath.item].mediaURL)")
cell.photoView.kf.setImage(with: mediaURL, options: [.targetCache(mediaCache)])
cell.photoView.isHidden = false
cell.videoView.isHidden = true
} else if(HomeControllerID.userFeed.userFeed[feedCellID!].userMedia![indexPath.item].mediaType == .video) {
let mediaThumbURL = URL(string: "https://myfileserver.com/media/\(HomeControllerID.userFeed.userFeed[feedCellID!].feedID!)/\(HomeControllerID.userFeed.userFeed[feedCellID!].userMedia![indexPath.item].mediaThumbURL!)")
let mediaURL = URL(string: "https://myfileserver.com/media/\(HomeControllerID.userFeed.userFeed[feedCellID!].feedID!)/\(HomeControllerID.userFeed.userFeed[feedCellID!].userMedia![indexPath.item].mediaURL)")!
cell.videoView.placeholderView.kf.setImage(with: mediaThumbURL, options: [.targetCache(mediaCache)])
cell.videoView.mediaURL = mediaURL
cell.photoView.isHidden = true
cell.videoView.isHidden = false
}
}
return cell
}

MediaSLiderCell 是一些非常基本的 UICollectionViewCell 东西:

class MediaSliderCell: UICollectionViewCell {
override func prepareForReuse() {
super.prepareForReuse()
photoView.isHidden = false
videoView.isHidden = true
videoView.mediaURL = nil
videoView.placeholderView.kf.cancelDownloadTask()
videoView.placeholderView.image = UIImage()
photoView.image = UIImage()
}

var photoView: UIImageView = {
let photoView = UIImageView()
photoView.translatesAutoresizingMaskIntoConstraints = false
photoView.backgroundColor = .black
photoView.isHidden = true
return photoView
}()

var videoView: VideoView = {
let videoView = VideoView()
videoView.translatesAutoresizingMaskIntoConstraints = false
videoView.backgroundColor = .black
return videoView
}()

override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}

func setupViews() {
addSubview(photoView)
addSubview(videoView)
photoView.topAnchor.constraint(equalTo: topAnchor).isActive = true
photoView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
photoView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
photoView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
videoView.topAnchor.constraint(equalTo: topAnchor).isActive = true
videoView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
videoView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
videoView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

我认为以上都可以。但是,对于 VideoView,我使用了一个自定义的 UIView,我在其中设置了一个占位符 (UIImageView)。下面有很多代码,但大多数是控件和用户界面的东西。重要的是,我使用了两个库:CacheCachingPlayerItem .这是为了避免一遍又一遍地下载相同的视频,所以当在缓存中找不到视频时,我下载该项目,当视频下载后,我将其保存在缓存中以备后用。我想一切都说得通。我觉得问题出在某个地方,或者在 AVPlayer 本身。看一下代码:

class VideoView: UIView, CachingPlayerItemDelegate {
var playerItem: CachingPlayerItem?
func playerItem(_ playerItem: CachingPlayerItem, didFinishDownloadingData data: Data) {
// A track is downloaded. Saving it to the cache asynchronously.
print("Saving video to cache on device")
storage?.async.setObject(data, forKey: mediaURL.absoluteString, completion: { _ in} )
}
var playerLooper: NSObject?
var playerLayer: AVPlayerLayer!
var queuePlayer: AVQueuePlayer?
var mediaURL: URL!
var placeholderView: UIImageView = {
let placeholderView = UIImageView()
placeholderView.translatesAutoresizingMaskIntoConstraints = false
placeholderView.backgroundColor = .black
return placeholderView
}()

var playerView: UIView = {
let playerView = UIView()
playerView.translatesAutoresizingMaskIntoConstraints = false
playerView.backgroundColor = .clear
playerView.isHidden = true
return playerView
}()

var playButton: UIImageView = {
let playButton = UIImageView()
playButton.image = UIImage(named: "playButton")
playButton.translatesAutoresizingMaskIntoConstraints = false
playButton.isUserInteractionEnabled = true
playButton.backgroundColor = .clear
return playButton
}()

var pauseButton: UIImageView = {
let pauseButton = UIImageView()
pauseButton.image = UIImage(named: "pauseButton")
pauseButton.translatesAutoresizingMaskIntoConstraints = false
pauseButton.backgroundColor = .clear
pauseButton.isUserInteractionEnabled = true
pauseButton.alpha = 0
return pauseButton
}()

var volumeOnButton: UIImageView = {
let volumeOnButton = UIImageView()
volumeOnButton.image = UIImage(named: "volumeOn")
volumeOnButton.translatesAutoresizingMaskIntoConstraints = false
volumeOnButton.backgroundColor = .clear
volumeOnButton.isUserInteractionEnabled = true
volumeOnButton.alpha = 0
return volumeOnButton
}()

var volumeOffButton: UIImageView = {
let volumeOffButton = UIImageView()
volumeOffButton.image = UIImage(named: "volumeOff")
volumeOffButton.translatesAutoresizingMaskIntoConstraints = false
volumeOffButton.backgroundColor = .clear
volumeOffButton.isUserInteractionEnabled = true
volumeOffButton.alpha = 0
return volumeOffButton
}()

override init(frame: CGRect) {
super.init(frame: frame)
self.setupViews()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setupViews()
}

func setupViews() {
addSubview(placeholderView)
placeholderView.topAnchor.constraint(equalTo: topAnchor).isActive = true
placeholderView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
placeholderView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
placeholderView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
addSubview(playerView)
playerView.topAnchor.constraint(equalTo: topAnchor).isActive = true
playerView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
playerView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
playerView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
let tapView = UITapGestureRecognizer(target: self, action: #selector(showControls))
playerView.addGestureRecognizer(tapView)
addSubview(playButton)
playButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
playButton.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
playButton.heightAnchor.constraint(equalToConstant: profilePicWidth * 2).isActive = true
playButton.widthAnchor.constraint(equalToConstant: profilePicWidth * 2).isActive = true
let tapPlayButton = UITapGestureRecognizer(target: self, action: #selector(playVideo))
playButton.addGestureRecognizer(tapPlayButton)
addSubview(pauseButton)
pauseButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
pauseButton.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
pauseButton.heightAnchor.constraint(equalToConstant: profilePicWidth * 2).isActive = true
pauseButton.widthAnchor.constraint(equalToConstant: profilePicWidth * 2).isActive = true
let tapPauseButton = UITapGestureRecognizer(target: self, action: #selector(pauseVideo))
pauseButton.addGestureRecognizer(tapPauseButton)
addSubview(volumeOnButton)
volumeOnButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -(normalSpacing + 2)).isActive = true
volumeOnButton.topAnchor.constraint(equalTo: topAnchor, constant: normalSpacing + 2).isActive = true
volumeOnButton.heightAnchor.constraint(equalToConstant: 32).isActive = true
volumeOnButton.widthAnchor.constraint(equalToConstant: 32).isActive = true
let tapVolumeOnButton = UITapGestureRecognizer(target: self, action: #selector(volumeAction))
volumeOnButton.addGestureRecognizer(tapVolumeOnButton)
addSubview(volumeOffButton)
volumeOffButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -(normalSpacing + 2)).isActive = true
volumeOffButton.topAnchor.constraint(equalTo: topAnchor, constant: normalSpacing + 2).isActive = true
volumeOffButton.heightAnchor.constraint(equalToConstant: 32).isActive = true
volumeOffButton.widthAnchor.constraint(equalToConstant: 32).isActive = true
let tapVolumeOffButton = UITapGestureRecognizer(target: self, action: #selector(volumeAction))
volumeOffButton.addGestureRecognizer(tapVolumeOffButton)
}

@objc func volumeAction() {
buttonTimer?.invalidate()
volumeTimer?.invalidate()
if UserDefaults.exists(key: "volumeOn") {
if(UserDefaults.standard.bool(forKey: "volumeOn") == false) {
self.queuePlayer?.isMuted = false
UserDefaults.standard.set(true, forKey: "volumeOn")
self.volumeOnButton.alpha = 1
self.volumeOffButton.alpha = 0
} else {
self.queuePlayer?.isMuted = true
UserDefaults.standard.set(false, forKey: "volumeOn")
self.volumeOnButton.alpha = 0
self.volumeOffButton.alpha = 1
}
} else {
self.queuePlayer?.isMuted = false
UserDefaults.standard.set(true, forKey: "volumeOn")
self.volumeOnButton.alpha = 1
self.volumeOffButton.alpha = 0
}
volumeTimer = Timer.scheduledTimer(timeInterval: 2.5, target: self, selector: #selector(fadeVolumeButton), userInfo: nil, repeats: false)
buttonTimer = Timer.scheduledTimer(timeInterval: 2.5, target: self, selector: #selector(fadePauseButton), userInfo: nil, repeats: false)
}

@objc func checkVolume() {
if UserDefaults.exists(key: "volumeOn") {
if(UserDefaults.standard.bool(forKey: "volumeOn") == false) {
self.queuePlayer?.isMuted = true
} else {
self.queuePlayer?.isMuted = false
}
} else {
self.queuePlayer?.isMuted = true
}
}

@objc func showControls() {
buttonTimer?.invalidate()
volumeTimer?.invalidate()
if(self.volumeOnButton.alpha > 0 || self.volumeOffButton.alpha > 0) {
UIView.animate(withDuration: 0.2) {
self.volumeOnButton.alpha = 0
self.volumeOffButton.alpha = 0
}
} else {
if UserDefaults.exists(key: "volumeOn") {
if(UserDefaults.standard.bool(forKey: "volumeOn") == false) {
UIView.animate(withDuration: 0.2) {
self.volumeOffButton.alpha = 1
self.volumeOnButton.alpha = 0
}
} else {
UIView.animate(withDuration: 0.2) {
self.volumeOffButton.alpha = 0
self.volumeOnButton.alpha = 1
}
}
} else {
UIView.animate(withDuration: 0.2) {
self.volumeOffButton.alpha = 1
self.volumeOnButton.alpha = 0
}
}
volumeTimer = Timer.scheduledTimer(timeInterval: 2.5, target: self, selector: #selector(fadeVolumeButton), userInfo: nil, repeats: false)
}

if(self.queuePlayer?.timeControlStatus == .playing) {
if(self.pauseButton.alpha > 0) {
UIView.animate(withDuration: 0.2) { self.pauseButton.alpha = 0 }
} else {
UIView.animate(withDuration: 0.2) { self.pauseButton.alpha = 1 }
buttonTimer = Timer.scheduledTimer(timeInterval: 2.5, target: self, selector: #selector(fadePauseButton), userInfo: nil, repeats: false)
}
} else if(self.queuePlayer?.timeControlStatus == .paused) {
if(self.playButton.alpha > 0) {
UIView.animate(withDuration: 0.2) { self.pauseButton.alpha = 0 }
} else {
UIView.animate(withDuration: 0.2) { self.playButton.alpha = 1 }
}
}
}

lazy var storage: Cache.Storage? = {
return try? Storage(diskConfig: diskConfig, memoryConfig: memoryConfig, transformer: TransformerFactory.forData())
}()

func loadVideo() {
// Trying to retrieve a track from cache asynchronously.
storage?.async.entry(forKey: mediaURL.absoluteString, completion: { result in
switch result {
case .error:
// The track is not cached.
print("Downloading from network")
self.playerItem = CachingPlayerItem(url: self.mediaURL)
case .value(let entry):
// The track is cached.
print("Downloading from cached library on device")
self.playerItem = CachingPlayerItem(data: entry.object, mimeType: "video/mp4", fileExtension: "mp4")
}
self.playerItem?.delegate = self
DispatchQueue.main.async {
if let playerItem = self.playerItem {
self.queuePlayer = AVQueuePlayer(items: [playerItem])
self.queuePlayer?.automaticallyWaitsToMinimizeStalling = false
self.playerLayer = AVPlayerLayer(player: self.queuePlayer)
self.playerLooper = AVPlayerLooper(player: self.queuePlayer!, templateItem: playerItem)
self.playerView.layer.addSublayer(self.playerLayer!)
self.playerLayer?.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.width * 1.25)
self.checkVolume()
do {
try AVAudioSession.sharedInstance().setCategory(.playback)
} catch(let error) {
print(error.localizedDescription)
}
self.queuePlayer?.play()
self.queuePlayer?.addObserver(self, forKeyPath: "timeControlStatus", options: .initial, context:nil)
}
}
})
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if(keyPath == "timeControlStatus") {
if(self.playerView.isHidden && self.queuePlayer?.timeControlStatus == .playing) {
self.playerView.isHidden = false
}
}
}

var buttonTimer: Timer?
var volumeTimer: Timer?

@objc func playVideo() {
buttonTimer?.invalidate()
volumeTimer?.invalidate()
if(self.queuePlayer?.currentItem == nil) {
self.loadVideo()
}
if(self.queuePlayer?.timeControlStatus == .paused) {
do {
try AVAudioSession.sharedInstance().setCategory(.playback)
} catch(let error) {
print(error.localizedDescription)
}
self.queuePlayer?.play()
}
self.playButton.alpha = 0
buttonTimer = Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(fadePauseButton), userInfo: nil, repeats: false)
volumeTimer = Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(fadeVolumeButton), userInfo: nil, repeats: false)
}

@objc func pauseVideo() {
buttonTimer?.invalidate()
self.queuePlayer?.pause()
self.playButton.alpha = 1
self.pauseButton.alpha = 0
self.volumeOnButton.alpha = 0
self.volumeOffButton.alpha = 0
}

@objc func fadePauseButton() {
UIView.animate(withDuration: 0.8) {
self.pauseButton.alpha = 0
}
}

@objc func fadeVolumeButton() {
UIView.animate(withDuration: 0.8) {
self.volumeOnButton.alpha = 0
self.volumeOffButton.alpha = 0
}
}
}

在我看来,问题应该出在loadVideo()上面的某个地方。但是,我提供了自定义 UIView VideoView 的所有代码,以避免任何错误或其他重要部分。谁能帮助我?会对我有很大帮助,因为我已经研究了好几个星期了。我已经尝试了每一个关于出列的建议,我已经尝试了所有我可以重用单元格的方法,......但都没有用。所以我想解决方案应该在AVPlayerCache 库或CachingPlayerItem 库中找到。将不胜感激任何帮助或建议。提前谢谢你。

作为记录,我忘记了一些事情,但应该从代码中清楚:未显示 AVPlayer,因此最初未调用 loadVideo()。仅当用户点击播放按钮 (playVideo()) 时。如果我也不播放视频,一切都会顺利。照片显示正确,占位符显示正确,但视频在 我开始播放一个或多个视频(点击播放按钮,调用 playVideo() ,它调用 loadVideo())。

编辑:好的,我想通了。当单元格被重用时,视频本身似乎没有改变。我的意思是,在 loadVideo() 中,AVQueuePlayer() 被设置,这就是改变之后显示和播放的视频的原因。我觉得我需要设置这个 block :

    // Trying to retrieve a track from cache asynchronously.
storage?.async.entry(forKey: mediaURL.absoluteString, completion: { result in

switch result {
case .error:
// The track is not cached.
print("Downloading from network")
DispatchQueue.main.async {
self.playerItem = CachingPlayerItem(url: self.mediaURL)
}
case .value(let entry):
// The track is cached.
print("Downloading from cached library on device")
DispatchQueue.main.async {
self.playerItem = CachingPlayerItem(data: entry.object, mimeType: "video/mp4", fileExtension: "mp4")
}
}

self.playerItem?.delegate = self

DispatchQueue.main.async {

if let playerItem = self.playerItem {
self.queuePlayer = AVQueuePlayer(items: [playerItem])
self.queuePlayer?.automaticallyWaitsToMinimizeStalling = false
self.playerLayer = AVPlayerLayer(player: self.queuePlayer)
self.playerLooper = AVPlayerLooper(player: self.queuePlayer!, templateItem: playerItem)
self.playerView.layer.addSublayer(self.playerLayer!)
self.playerLayer?.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.width * 1.25)
self.checkVolume()
do {
try AVAudioSession.sharedInstance().setCategory(.playback)
} catch(let error) {
print(error.localizedDescription)
}
self.queuePlayer?.addObserver(self, forKeyPath: "timeControlStatus", options: .initial, context:nil)

}

}

})

在其他地方,在视频播放之前。因为这只会在点击播放按钮时被调用,但随后会显示错误的视频。已尝试直接在单元格中调用 loadVideo(),但似乎无法解决问题。我觉得我越来越近了。有什么想法吗?

最佳答案

不要将 AVQueuePlayer 存储在 tableView 单元格中。请记住,您可以保留的 AVPlayer 实例数量有上限。

将播放器本身、他们的配置、开始、暂停以及您拥有的任何其他操作移动到存储您的 UITableViewUIViewController 中。

当用户在 UITableViewCell 上按下播放时,您将通过 delegate 告诉 UIViewController 开始播放和 UIViewController 会做类似的事情:

  1. 取消之前的播放
  2. 使用播放器项目填充 AVQueuePlayer
  3. AVQueuePlayer插入UITableViewCell
  4. 开始演奏

不要忘记在重用单元格时从 UITableViewCell 中删除 AVQueuePlayer

关于ios - 对自定义 AVPlayer 的错误引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59225341/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com