Swift 3 - CollectionView 数据源未返回有效单元格

Posted

技术标签:

【中文标题】Swift 3 - CollectionView 数据源未返回有效单元格【英文标题】:Swift 3 - CollectionView data source did not return a valid cell 【发布时间】:2017-06-27 15:25:23 【问题描述】:

我正在使用此代码:https://www.youtube.com/watch?v=bNtsekO51iQ,但是当我实现我的数据并使用 collectionView.reloadData() 时,它会崩溃并出现错误代码 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'the collection view's data source did not return a valid cell from -collectionView:cellForItemAtIndexPath: for index path <NSIndexPath: 0xc000000000000016> length = 2, path = 0 - 0'

class ChatLogController: UICollectionViewController, UICollectionViewDelegateFlowLayout 

    var userId:Int?
    var position:Int = 0
    var otherAvatar:UIImage = UIImage(named: "defaultAvatar")!
    var otherName:String = ""
    var otherSex:String = ""
    var otherBanned:Int = 0
    var otherBlocked:Int = 0
    var messagesDates:[String] = []
    var messagesText:[String] = []
    var messagesIds:[String] = []
    var messagesPics:[UIImage?] = []
    var messagesSeen:[Int] = []
    var messagesWhoSendIt:[Int] = []
    private let cellId = "cellId"
    override func viewDidLoad() 
        super.viewDidLoad()
        collectionView?.register(ChatLogMessageCell.self, forCellWithReuseIdentifier: cellId)
        collectionView!.isPrefetchingEnabled = false
        loadChatsFor(position: position)
        override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
        return messagesIds.count
    

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell 
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath as IndexPath) as! ChatLogMessageCell

        cell.messageTextView.text = messagesText[indexPath.row]


            cell.profileImageView.image = UIImage(named: "defaultAvatar")!

            let size = CGSize(width:250,height:1000)
            let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
            let estimatedFrame = NSString(string: messagesText[indexPath.row]).boundingRect(with: size, options: options, attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 18)], context: nil)

        cell.messageTextView.frame = CGRect(x:48 + 8, y:0, width:estimatedFrame.width + 16, height:estimatedFrame.height + 20)

        cell.textBubbleView.frame = CGRect(x:48, y:0, width:estimatedFrame.width + 16 + 8, height:estimatedFrame.height + 20)

        return cell
    

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
            let size = CGSize(width:250,height:1000)
            let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
            let estimatedFrame = NSString(string: messagesText[indexPath.row]).boundingRect(with: size, options: options, attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 18)], context: nil)

        return CGSize(width:view.frame.width, height:estimatedFrame.height + 20)
    


    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets 
        return UIEdgeInsetsMake(8, 0, 0, 0)
    



class ChatLogMessageCell: BaseCell 

    let messageTextView: UITextView = 
        let textView = UITextView()
        textView.font = UIFont.systemFont(ofSize: 18)
        textView.text = "Sample message"
        textView.backgroundColor = UIColor.clear
        return textView
    ()

    let textBubbleView: UIView = 
        let view = UIView()
        view.backgroundColor = UIColor(white: 0.95, alpha: 1)
        view.layer.cornerRadius = 15
        view.layer.masksToBounds = true
        return view
    ()

    let profileImageView: UIImageView = 
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFill
        imageView.layer.cornerRadius = 15
        imageView.layer.masksToBounds = true
        return imageView
    ()

    override func setupViews() 
        super.setupViews()

        addSubview(textBubbleView)
        addSubview(messageTextView)

        addSubview(profileImageView)
        addConstraintsWithFormat(format:"H:|-8-[v0(30)]", views: profileImageView)
        addConstraintsWithFormat(format:"V:[v0(30)]|", views: profileImageView)
        profileImageView.backgroundColor = UIColor.red
    


extension UIView 

    func addConstraintsWithFormat(format: String, views: UIView...) 

        var viewsDictionary = [String: UIView]()
        for (index, view) in views.enumerated() 
            let key = "v\(index)"
            viewsDictionary[key] = view
            view.translatesAutoresizingMaskIntoConstraints = false
        

        addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
    



class BaseCell: UICollectionViewCell 
    override init(frame: CGRect) 
        super.init(frame: frame)
        setupViews()
    

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

    func setupViews() 
    

在 loadChatsFor 中,我从 web 获取数组数据并使用 collectionView.reloadData(),但是当执行此函数时,它会使我的应用程序崩溃。我一直在寻找答案,但没有成功。我添加了 ios 10 函数 collectionView!.isPrefetchingEnabled = false 视图确实从这个答案UICollectionView exception in UICollectionViewLayoutAttributes from iOS7 加载,但也没有工作。即使在 reloadData 之前和 reloadData 之后的方法 collectionViewLayout invalidateLayout 也不能​​阻止崩溃。那么我还能做些什么来让它发挥作用呢?

我从 UITableViewCell 进入这个 CollectionViewController

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
    let layout = UICollectionViewFlowLayout()
    let controller = ChatLogController(collectionViewLayout: layout)
    controller.userId = chatUserIds[indexPath.row]
    navigationController?.pushViewController(controller, animated: true)

我在tableview的类中添加了UICollectionViewDelegateFlowLayout

【问题讨论】:

【参考方案1】:

您没有实现正确的cellForItemAt 方法UICollectionViewDataSource。这个方法的签名在 Swift 3 中是这样改变的。

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath as IndexPath) as! ChatLogMessageCell

    cell.messageTextView.text = messagesText[indexPath.row]


    cell.profileImageView.image = UIImage(named: "defaultAvatar")!

    let size = CGSize(width:250,height:1000)
    let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
    let estimatedFrame = NSString(string: messagesText[indexPath.row]).boundingRect(with: size, options: options, attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 18)], context: nil)

    cell.messageTextView.frame = CGRect(x:48 + 8, y:0, width:estimatedFrame.width + 16, height:estimatedFrame.height + 20)

    cell.textBubbleView.frame = CGRect(x:48, y:0, width:estimatedFrame.width + 16 + 8, height:estimatedFrame.height + 20)

    return cell

【讨论】:

我点击迁移到 Swift 3,但这可能是 XCode 8.1 中的错误,非常感谢 :)

以上是关于Swift 3 - CollectionView 数据源未返回有效单元格的主要内容,如果未能解决你的问题,请参考以下文章

Swift 3 - CollectionView 数据源未返回有效单元格

Swift 3.0:自定义CollectionView标题按钮单击

SWIFT 3:将不同的声音文件数组匹配到 CollectionView 中的每个部分

Swift 3:在 CollectionView 中选择时放大图像

Swift 3 collectionView 如何设置间距。

Swift 3 - NSLayoutConstraint CollectionView 附加到另一个视图