当我将滚动方向从垂直更改为水平时,为啥集合视图会崩溃?

Posted

技术标签:

【中文标题】当我将滚动方向从垂直更改为水平时,为啥集合视图会崩溃?【英文标题】:Why does collection view crash when I change the scroll direction from vertical to horizontal?当我将滚动方向从垂直更改为水平时,为什么集合视图会崩溃? 【发布时间】:2020-01-16 20:18:35 【问题描述】:

我有一个集合视图,它显示了我传递给它的任何数据对象的一组视频。当您在内容视图中按下其中一个视频时,会出现一个新视图 (DetailViewController),并且视频会以更详细的视图显示。在那个 DetailViewController 中有一个后退按钮,可以关闭视图并将您带回带有集合视图的主页 - 这是发生崩溃时,当我从那个 'DetailViewController' 返回主视图控制器时。

当集合视图的“滚动方向”设置为垂直时,一切正常,但是当我将它设置为水平(这是我想要的)时,它会像上面解释的那样崩溃。

崩溃是:

Unexpectedly found nil while unwrapping an Optional value

来源是:

private func findCurrentCell(path: IndexPath) -> UICollectionViewCell 
    return playerCollect.cellForItem(at: path)!

我猜pathIndexPath 在用户滚动时没有更新,因此索引没有更新?

有什么想法吗?如果需要,我可以提供视频或一些额外的代码。

编辑(额外代码):

import UIKit
import AVFoundation
import MMPlayerView

class ViewController: UIViewController 
    var offsetObservation: NSKeyValueObservation?
    lazy var mmPlayerLayer: MMPlayerLayer = 
    let l = MMPlayerLayer()        
    l.cacheType = .memory(count: 5)
    l.coverFitType = .fitToPlayerView
    l.videoGravity = AVLayerVideoGravity.resizeAspect
    l.replace(cover: CoverA.instantiateFromNib())
    l.repeatWhenEnd = true
    return l
()

@IBOutlet weak var playerCollect: UICollectionView!
override func viewDidLoad() 
    super.viewDidLoad()
    // remove previous download fails file
    MMPlayerDownloader.cleanTmpFile()
    self.navigationController?.mmPlayerTransition.push.pass(setting:  (_) in

    )
    offsetObservation = playerCollect.observe(\.contentOffset, options: [.new])  [weak self] (_, value) in
        guard let self = self, self.presentedViewController == nil else return
        NSObject.cancelPreviousPerformRequests(withTarget: self)
        self.perform(#selector(self.startLoading), with: nil, afterDelay: 0.2)
    
    playerCollect.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 200, right:0)
    DispatchQueue.main.asyncAfter(deadline: .now() + 1.0)  [weak self] in
        self?.updateByContentOffset()
        self?.startLoading()
    

    mmPlayerLayer.getStatusBlock  [weak self] (status) in
        switch status 
        case .failed(let err):
            let alert = UIAlertController(title: "err", message: err.description, preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
            self?.present(alert, animated: true, completion: nil)
        case .ready:
            print("Ready to Play")
        case .playing:
            print("Playing")
        case .pause:
            print("Pause")
        case .end:
            print("End")
        default: break
        
    
    mmPlayerLayer.getOrientationChange  (status) in
        print("Player OrientationChange \(status)")
    


deinit 
    offsetObservation?.invalidate()
    offsetObservation = nil
    print("ViewController deinit")




func backReplaceSuperView(original: UIView?) -> UIView? 
    guard let path = self.findCurrentPath() else 
        return original
    

    let cell = self.findCurrentCell(path: path) as! PlayerCell
    return cell.imgView


// add layer to temp view and pass to another controller
var passPlayer: MMPlayerLayer 
    return self.mmPlayerLayer

func transitionWillStart() 

// show cell.image
func transitionCompleted() 
    self.updateByContentOffset()
    self.startLoading()



extension ViewController: UICollectionViewDelegateFlowLayout 
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    let m = min(UIScreen.main.bounds.size.width, UIScreen.main.bounds.size.height)
    return CGSize(width: m, height: m*0.75)

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
   DispatchQueue.main.async  [unowned self] in
    if self.presentedViewController != nil || self.mmPlayerLayer.isShrink == true 
            //self.playerCollect.scrollToItem(at: indexPath, at: .centeredVertically, animated: true)
            //self.updateDetail(at: indexPath)
         else 
            self.presentDetail(at: indexPath)
        
    


fileprivate func updateByContentOffset() 
    if mmPlayerLayer.isShrink 
        return
    

    if let path = findCurrentPath(),
        self.presentedViewController == nil 
        self.updateCell(at: path)
        //Demo SubTitle
        if path.row == 0, self.mmPlayerLayer.subtitleSetting.subtitleType == nil 
            let subtitleStr = Bundle.main.path(forResource: "srtDemo", ofType: "srt")!
            if let str = try? String.init(contentsOfFile: subtitleStr) 
                self.mmPlayerLayer.subtitleSetting.subtitleType = .srt(info: str)
                self.mmPlayerLayer.subtitleSetting.defaultTextColor = .red
                self.mmPlayerLayer.subtitleSetting.defaultFont = UIFont.boldSystemFont(ofSize: 20)
            
        
    


fileprivate func presentDetail(at indexPath: IndexPath) 
    self.updateCell(at: indexPath)
    mmPlayerLayer.resume()

    if let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DetailViewController") as? DetailViewController 
        vc.data = DemoSource.shared.demoData[indexPath.row]
        self.present(vc, animated: true, completion: nil)
    


fileprivate func updateCell(at indexPath: IndexPath) 
    if let cell = playerCollect.cellForItem(at: indexPath) as? PlayerCell, let playURL = cell.data?.play_Url 
        // this thumb use when transition start and your video dosent start
        mmPlayerLayer.thumbImageView.image = cell.imgView.image
        // set video where to play
        mmPlayerLayer.playView = cell.imgView
        mmPlayerLayer.set(url: playURL)
    


@objc fileprivate func startLoading() 
    self.updateByContentOffset()
    if self.presentedViewController != nil 
        return
    
    // start loading video
    mmPlayerLayer.resume()


private func findCurrentPath() -> IndexPath? 
    let p = CGPoint(x: playerCollect.frame.width/2, y: playerCollect.contentOffset.y + playerCollect.frame.width/2)
    return playerCollect.indexPathForItem(at: p)


private func findCurrentCell(path: IndexPath) -> UICollectionViewCell 
    return playerCollect.cellForItem(at: path)!



extension ViewController: UICollectionViewDataSource 
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    return DemoSource.shared.demoData.count


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PlayerCell", for: indexPath) as? PlayerCell 
        cell.data = DemoSource.shared.demoData[indexPath.row]
        return cell
    
    return UICollectionViewCell()


【问题讨论】:

因为您使用了强制的可选运算符!。强制可选运算符被认为是不好的做法,并且通常会导致您在上面列出的确切错误消息。您需要检查player.cellForItem(at: path) 是否实际返回nil 以外的值。 我不需要它来返回一个值,考虑到当我在垂直模式下使用集合视图时它工作得很好? 你能提供更多代码吗?您包含的方法不是UICollectionViewDelegate 方法之一,因此了解您如何使用findCurrentCell 很重要,因为您传递的indexPathplayerCollect 集合视图中显然不是有效的方法 您可以返回 UICollectionViewCell? 并测试/调试您得到的结果而不会崩溃。 (因此不使用!。) @NoodleOfDeath 一定要检查我的新问题 【参考方案1】:

使用强制可选运算符! 是一种不好的做法,因为它通常会导致您描述的错误消息。您的findCurrentPath 计算尚未针对水平移动进行更新。首先,将您的 findCurrentPath 方法更改为(我相信您正在努力实现的目标):

private func findCurrentPath() -> IndexPath? 
    let p = CGPoint(x: playerCollect.contentOffset.x + playerCollect.frame.width/2, 
                    y: playerCollect.frame.height/2)
    return playerCollect.indexPathForItem(at: p)

为了防止崩溃,即使它没有完全按照你的意愿做,我只清理了另外两种方法,因为有太多的强制可选运算符来解决它们;将您的 findCurrentCell 方法更改为以下内容:

private func findCurrentCell(path: IndexPath) -> UICollectionViewCell? 
   return playerCollect.cellForItem(at: path)

最后是你的backReplaceSuperView 方法:

 func backReplaceSuperView(original: UIView?) -> UIView? 
    guard let path = self.findCurrentPath(),
        let cell = self.findCurrentCell(path: path) as? PlayerCell else 
        return original
    
    return cell.imgView

【讨论】:

太完美了!谢谢,正是我需要的!

以上是关于当我将滚动方向从垂直更改为水平时,为啥集合视图会崩溃?的主要内容,如果未能解决你的问题,请参考以下文章

iOS 9 UICollectionView 水平重新加载数据不起作用

将方向更改为“水平”不会更改 RadListView 的滚动方向?

当我开始水平滚动时,为啥我的收藏视图中的单元格被取消选择?

android webview没有水平滚动

UIScrollView 不使用 UICollectionView 滚动

将excel另存为pdf,将其方向更改为水平