0

I am using this code: https://www.youtube.com/watch?v=bNtsekO51iQ , but when I implement my data and use collectionView.reloadData() it crashes with error code *** 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() {
    }
}

In loadChatsFor I get the array data from web and I use collectionView.reloadData(), but when this function is performed it crashes my app. I've searched for answer, but unsuccessfully. I've added IOS 10 function collectionView!.isPrefetchingEnabled = false in view did load from this answer UICollectionView exception in UICollectionViewLayoutAttributes from iOS7, but also not working. Even the method collectionViewLayout invalidateLayout before reloadData and after reloadData doesn't stop the crash. So what else I can do to make it work ?

I am coming in this CollectionViewController from UITableViewCell

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)
}

I've added UICollectionViewDelegateFlowLayout in the class of the tableview

Community
  • 1
  • 1
Bogdan Bogdanov
  • 882
  • 11
  • 36
  • 79

1 Answers1

1

You are not implementing correct cellForItemAt method of UICollectionViewDataSource. Signature of this method is changed in Swift 3 like this.

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
}
Nirav D
  • 71,513
  • 12
  • 161
  • 183