UICollectionView cellForItem(at:) 崩溃

Posted

技术标签:

【中文标题】UICollectionView cellForItem(at:) 崩溃【英文标题】:UICollectionView cellForItem(at:) crashes 【发布时间】:2018-01-25 10:20:21 【问题描述】:

我正在使用 Swift4 和 Xcode9。

我使用下面的委托方法创建了我的 collectionView 单元格;

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mediaItemCell", for: indexPath) as! MediaItemCollectionViewCell
    cell.mediaItemCellImageView.image = mediaPlayer?.item(at: indexPath.row)?.image

    return cell

然后,我想用“停止”图像替换所选单元格的图像。为此,我尝试使用didSelectItemAt 方法;

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 

        if (mediaPlayer?.isPlaying == true) 
            // stop the player
            mediaPlayer?.stopPlayingMedia()
         else 
            // change the image and start playing
            let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell // CRASH
            cell.mediaItemCellImageView.image = UIImage(named: "stop.png")

            ...
            ...
        
    

func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) 
    // restore the original image
    let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell
    cell.mediaItemCellImageView.image = mediaPlayer?.item(at: indexPath.row)?.image
    // stop playing
    mediaPlayer?.stopPlayingMedia()

现在,当我选择已选择的单元格(因此原始图像已被“stop.png”替换)时,上面的代码有效;即恢复原图,通过stopPlayingMedia()方法完成。

当我选择与当前选择的单元格不同的单元格时,应用程序崩溃。我试图将didSelectItemAt 调用移出委托方法,但没有成功。

令人惊讶的是,当我选择当前选定的单元格时,逻辑检查if collectionView.cellForItem(at: indexPath) is MediaItemCollectionViewCell 成功,而当我选择另一个单元格时失败。

我想更改单元格的图像,因此需要转换变量类型但它失败了。为什么当我选择与当前选择的单元格不同的单元格时,投射失败?

谢谢

编辑:

我使用 collectionView.reloadData() 将所有图像恢复为原始图像(我在 OP 中提到了这一点 - 当调用 stopPlayingMedia() 时)。看来,如果我将private var index:IndexItem? 添加到我的ViewController 类并在didSelectItemAt 中更新其值(以保存最后选择的单元格)并使用下面的代码,则代码不会崩溃

extension ViewController:MediaPlayerControllerDelegate 
// this delegate method is invoked when stopPlayingMedia() is called
    func mediaPlayerControllerDidFinishPlaying() 
        // restore the UI
        // collectionView.reloadData() -> this causes the crash, instead, I only restore one cell
        let cell = collectionView.cellForItem(at: index!) as! MediaItemCollectionViewCell
        cell.mediaItemCellImageView.image = mediaPlayer?.item(at: (index?.row)!)?.image

        timeLabel.text = "00:00"
        progressBar.progress = 0.0
    

【问题讨论】:

它崩溃了?什么是崩溃日志?如果单元格不可见,cellForItem(at:) 也会返回 nil,所以很明显,在它之后执行 as! 并不是一个好主意,您应该执行 if let insetad。 您收到什么错误信息? 我收到Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value 错误。为什么我选择同一个单元格时强制转换成功但选择另一个单元格时失败? "为什么当我选择同一个单元格时强制转换成功,但在选择另一个单元格时失败?"因为如果单元格不可见。当它崩溃时,单元格是否可见?我不这么认为。 @Larme, print(collectionView.visibleCells.count) 当我选择当前选定的单元格时给出 12,当我选择另一个单元格时给出 0。我想我需要先澄清“可见细胞”的概念!任何解释将不胜感激! 【参考方案1】:

我尝试了以下方法来创建单元格并在选择时更改单元格内的图像。一点也不崩溃。请检查实现,它可能会帮助你。

import UIKit

class ViewController: UIViewController 
private var isPlayingModel = [false, false, false, false, false] // initially all are stop, not playing

@IBOutlet weak var collectionView: UICollectionView! 
    didSet
        collectionView.delegate = self
        collectionView.dataSource = self
    
 


extension ViewController: UICollectionViewDataSource 
 func numberOfSections(in collectionView: UICollectionView) -> Int 
    return 1
 

 func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    return isPlayingModel.count

 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mediaItemCell", for: indexPath) as! MediaItemCollectionViewCell
    cell.imageVw.image = isPlayingModel[indexPath.row] ? #imageLiteral(resourceName: "start") : #imageLiteral(resourceName: "stop")
    return cell



extension ViewController: UICollectionViewDelegate 
 func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 

    isPlayingModel[indexPath.row] = !isPlayingModel[indexPath.row]
    let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell
    cell.imageVw.image = isPlayingModel[indexPath.row] ? #imageLiteral(resourceName: "start") : #imageLiteral(resourceName: "stop")
  
 

extension ViewController: UICollectionViewDelegateFlowLayout 
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    return CGSize(width: view.frame.size.width, height: 60)

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat 
    return 4
 


 class MediaItemCollectionViewCell: UICollectionViewCell 
   @IBOutlet weak var imageVw: UIImageView!
 

Check the implementation here.

【讨论】:

【参考方案2】:

您没有正确传递 indexPath 来加载单元格,这就是它崩溃的原因。编辑您的 didSelectItemAt: 方法如下:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
          let selectedCell = IndexPath(item: indexPath.item, section: indexPath.section)
          let cell = collectionView.cellForItem(at: selectedCell) as! ACollectionViewCell
          cell.imgView.image = UIImage(named: "3.jpg")
     

【讨论】:

【参考方案3】:

集合视图 indexPath 具有元素 item,而不是 row

试试这个看看:(让我知道它打印了什么)

func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) 
    // restore the original image
    if let cell = collectionView.cellForItem(at: indexPath) as? MediaItemCollectionViewCell 
        if let cellImage = mediaPlayer?.item(at: indexPath.item)?.image  // Collection view indexPath has element `item`, and not `row`
             cell.mediaItemCellImageView.image = cellImage 
         else 
             print("cellImage not found at indexPath - \(indexPath)")
        

     else 
      print("collection view cell `MediaItemCollectionViewCell` not found at indexPath -\(indexPath) ")
    

    // stop playing
    mediaPlayer?.stopPlayingMedia()

【讨论】:

代码根本不打印任何东西,其实didDeselectItemAt方法就可以了。 那么问题出在哪里,如果它没有打印任何 else 块语句,那么我认为它应该可以正常工作,没有应用程序崩溃,对吧?您对此还有其他问题吗?

以上是关于UICollectionView cellForItem(at:) 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

如何滚动另一个 UICollectionView 下方的 UICollectionView?

UICollectionView 单元内部已加载,但 UICollectionView 未显示

UICollectionView 断言失败 -[UICollectionView _updateWithItems:tentativelyForReordering:]

UITableviewCell 中的 UICollectionView。添加约束后,UICollectionView 不显示

将标题添加到 UICollectionView,而不是 UICollectionView 的部分

嵌套垂直 UICollectionView 和动态高度