ios EXC_BAD_ACCESS(使用AVPlayer自动播放视频时)

neekobn8  于 2023-03-14  发布在  iOS
关注(0)|答案(1)|浏览(181)

所以我按照教程-https://mobiraft.com/ios/for-gods-sake-can-you-autoplay-video-in-list-ios/
接下来,由于我们没有“固定”的视频内容(只是偶尔)我决定不实现这个集合,只是把它作为一个PlayerView类,我在Home View控制器中创建了一个var videoCells[IndexPath : HomeVideoCell]()数组。当视图消失时,我清理了所有内容,因为我忘记了表单元格是重用的,然而,代码有一个deinit,应该删除它?我不太清楚为什么它需要再次加载内容,但无论如何。如果有人知道发生了什么事,我会喜欢一些帮助。我个人认为这是由于dequeueReusableCell,但我'我不确定滚动提要中的替代方案是什么
我用一个箭头指向它,但是错误层在PlayerView中,它说'if let layer = self.layer as?AVPlayerLayer
播放器视图

import Foundation
import UIKit
import AVKit

class PlayerView: UIView {
    private var url: URL?
    private var urlAsset: AVURLAsset?
    private var playerItem: AVPlayerItem?
    
    private var assetPlayer:AVPlayer? {
        didSet {
            DispatchQueue.main.async {
                if let layer = self.layer as? AVPlayerLayer { <-----
                    layer.player = self.assetPlayer
                }
            }
        }
    }
    
    override class var layerClass: AnyClass {
        return AVPlayerLayer.self
    }
    
    init() {
        super.init(frame: .zero)
        initialSetup()
    }
    
    required init?(coder: NSCoder) {
        super.init(frame: .zero)
        initialSetup()
    }
    
    private func initialSetup() {
        if let layer = self.layer as? AVPlayerLayer {
            // Do any configuration
            layer.videoGravity = AVLayerVideoGravity.resizeAspect
        }
    }
    
    func prepareToPlay(withUrl url:URL, shouldPlayImmediately: Bool = false) {
        guard !(self.url == url && assetPlayer != nil && assetPlayer?.error == nil) else {
            if shouldPlayImmediately {
                play()
            }
            return
        }
        
        cleanUp()
        
        self.url = url
        
        let options = [AVURLAssetPreferPreciseDurationAndTimingKey : true]
        let urlAsset = AVURLAsset(url: url, options: options)
        self.urlAsset = urlAsset
        
        let keys = ["tracks"]
        urlAsset.loadValuesAsynchronously(forKeys: keys, completionHandler: { [weak self] in
            guard let strongSelf = self else { return }
            strongSelf.startLoading(urlAsset, shouldPlayImmediately)
        })
        NotificationCenter.default.addObserver(self, selector: #selector(self.playerItemDidReachEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
    }
    
    private func startLoading(_ asset: AVURLAsset, _ shouldPlayImmediately: Bool = false) {
        var error:NSError?
        let status: AVKeyValueStatus = asset.statusOfValue(forKey: "tracks", error: &error)
        if status == AVKeyValueStatus.loaded {
            let item = AVPlayerItem(asset: asset)
            self.playerItem = item
            
            let player = AVPlayer(playerItem: item)
            self.assetPlayer = player
            
            if shouldPlayImmediately {
                DispatchQueue.main.async {
                    player.play()
                }
            }
        }
    }
    
    func play() {
        guard self.assetPlayer?.isPlaying == false else { return }
        DispatchQueue.main.async {
            self.assetPlayer?.play()
        }
    }
    
    func pause() {
        guard self.assetPlayer?.isPlaying == true else { return }
        DispatchQueue.main.async {
            self.assetPlayer?.pause()
        }
    }
    
    func cleanUp() {
        pause()
        urlAsset?.cancelLoading()
        urlAsset = nil
        assetPlayer = nil
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
    }
    
    deinit {
        cleanUp()
    }
    
    @objc private func playerItemDidReachEnd(_ notification: Notification) {
        guard notification.object as? AVPlayerItem == self.playerItem else { return }
        DispatchQueue.main.async {
            guard let videoPlayer = self.assetPlayer else { return }
            videoPlayer.seek(to: .zero)
            videoPlayer.play()
        }
    }
}

首页VC相关零件

class HomeVC: UIViewController,UIScrollViewDelegate,MFMailComposeViewControllerDelegate,CLLocationManagerDelegate {
    var videoCells = [IndexPath : HomeVideoCell]() //Variable is declared to be strong
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
               let videoCell = tableView.dequeueReusableCell(withIdentifier: "HomeVideoCell") as!  HomeVideoCell
                videoCell.selectionStyle = .none
                videoCell.playerView = PlayerView()
                videoCells[indexPath] = videoCell
                print("Video cell found and created PlayerView")
              //  videoCell.img_thumb.roundCorners([.topLeft, .bottomLeft], radius: 10)
                let str = self.bottomBannerData[indexPath.row]["video_thumb"]
                let videoStr = self.bottomBannerData[indexPath.row]["video"]
                
                print("strValue: \(String(describing: str)) -- videoStrValue: \(String(describing: videoStr))")
                if videoStr != "" {
                    if let videoUrl = URL(string: "\(imageURL)\(videoStr!)") {
                        videoCell.playerView?.prepareToPlay(withUrl: videoUrl)
                        print("VIDEO CELL Set PlayerView with URL \(videoUrl)")
                    }
                }
//                if str != "" {
//                    let url:NSURL = NSURL(string :"\(imageURL)\(str!)")!
//                    videoCell.img_thumb.af.setImage(withURL: url as URL)
//                }
                print("VIDEO CELL DONE")
                return videoCell
}

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        print("VDEO CELL VISIBLE")
        if let videoCell = videoCells[indexPath] {
            videoCell.playerView?.play()
            print("VIDEO CELL PLAY!")
        }
    }
    
    func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        if let videoCell = videoCells[indexPath] {
            videoCell.playerView?.pause()
        }
    }
juzqafwq

juzqafwq1#

在本例中,似乎PlayerView示例在重用视频单元格时也被误用,从而加载了新内容。这是因为HomeVC类中的videoCells字典。此字典包含每个视频单元格的键-值对,单元格的索引用作键。但是,在tableView(_ tableView:UITableView,索引处的单元格行路径:IndexPath)-〉UITableViewCell函数,每渲染一个单元格,就会在字典中添加一个新的HomeVideoCell,这样当显示一个新的单元格时(使用willDisplay函数),对应的视频单元格就会播放PlayerView,但是当重复使用同一个单元格时,就会用相同的索引重新创建视频单元格,并创建一个新的PlayerView示例,从而加载新的内容。
要解决此问题,在重用视频单元格时,不应创建新的PlayerView示例来替换现有的PlayerView示例。相反,cellForRowAt函数应查找当前单元格的上一个视频单元格是否存在,如果存在,则应重用该单元格,而不是当前视频单元格。为此,需要如下更新代码:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var videoCell: HomeVideoCell
if let cachedCell = videoCells[indexPath] {
// If a cached video cell exists for this index path, reuse it
videoCell = cachedCell
} else {
// Otherwise, create a new video cell and add it to the cache
videoCell = tableView.dequeueReusableCell(withIdentifier: "HomeVideoCell") as! HomeVideoCell
videoCell.selectionStyle = .none
videoCell.playerView = PlayerView()
videoCells[indexPath] = videoCell
print("Video cell found and created PlayerView")
}
// Set up the video player
let str = self.bottomBannerData[indexPath.row]["video_thumb"]
let videoStr = self.bottomBannerData[indexPath.row]["video"]
print("strValue: (String(describing: str)) -- videoStrValue: (String(describing: videoStr))")
if videoStr != "" {
if let videoUrl = URL(string: "(imageURL)(videoStr!)") {
videoCell.playerView?.prepareToPlay(withUrl: videoUrl)
print("VIDEO CELL Set PlayerView with URL (videoUrl)")
}
}
print("VIDEO CELL DONE")
return videoCell
}

我希望这能帮上忙。

相关问题