UICollectionView cells recalculating sizes (flicker) while scrolling

Viewed 1703

After initial table load, everything is working correctly. But after inserting a new cell, and scrolling fast up, you can see some cells recalculating their sizes (animating). It's really odd and happens to 2-3 cells max. I'm using a bottom-up collection view with reversed cells, and custom flow layout, but it has no animation either. Also when scrolling, keyboard hides, so it might be something to do with it..

Cell insert :

  self.collectionView?.insertItems(at: [indexPath])

  UIView.performWithoutAnimation {
      self.collectionView?.reloadItems(at: indexPaths)
      self.view.layoutIfNeeded()
  }
  self.collectionView?.setContentOffset(CGPoint(x: 0, y: 0), animated: true)

Video of the issue:

http://vimple.co/3c39cb325b9c4ec19173fea015b6cc8b

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

    let newIndex : Int = messagesDataArray.count - 1 - indexPath.item
    if messagesDataArray[newIndex].otherPerson == true {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ConversationCellOtherPerson", for: indexPath) as! ConversationCellOtherPerson
        cell.data = messagesDataArray[newIndex]
        cell.personImageView.image = otherPersonImage
        return cell
    } else {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ConversationCellUser", for: indexPath) as! ConversationCellUser
        cell.data = messagesDataArray[newIndex]
        return cell
    }

}

Also. ConversationCellUser:

class ConversationCellUser : UICollectionViewCell {

    let messageLabel = ConversationLabel()
    let mainContainerView = UIView()
    let textContainerView : UIView = {
        let view = UIView()
        view.layer.cornerRadius = 20
        return view
    }()

    var data : MessagesInfo? { didSet { updateCell() } }


    func updateCell() {
        guard let data = data else { return }
        messageLabel.text = data.messageText
    }

    override init(frame: CGRect) {
        super.init(frame: frame)

        backgroundColor = .clear
        clipsToBounds = true

        messageLabel.translatesAutoresizingMaskIntoConstraints = false
        textContainerView.translatesAutoresizingMaskIntoConstraints = false
        mainContainerView.translatesAutoresizingMaskIntoConstraints = false

        mainContainerView.addSubview(textContainerView)
        textContainerView.addSubview(messageLabel)
        contentView.addSubview(mainContainerView)



        NSLayoutConstraint(item: textContainerView, attribute: .right, relatedBy: .equal, toItem: mainContainerView, attribute: .right, multiplier: 1, constant: -10).isActive = true


        textContainerView.addConstraintsWithFormat(format: "H:|-14-[v0]-14-|", views: messageLabel)
        textContainerView.addConstraintsWithFormat(format: "V:|-10-[v0]-10-|", views: messageLabel)

        contentView.addConstraintsWithFormat(format: "H:|[v0]|", views: mainContainerView)
        contentView.addConstraintsWithFormat(format: "V:|[v0]|", views: mainContainerView)

        textContainerView.backgroundColor = .lightGray


    }

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

    override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
        super.apply(layoutAttributes)
        transform = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: 0)
        updateConstraints()
        setNeedsUpdateConstraints()

    }
}

ConversationLabel :

class ConversationLabel : BaseMessageLabel {

    override init(frame: CGRect) {
        super.init(frame: frame)

        self.textColor = .white
        self.font = UIFont(name: "AvenirNext-Medium", size: 14)
        self.lineBreakMode = .byWordWrapping
        self.numberOfLines = 0

        self.preferredMaxLayoutWidth = 200
    }

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

SizeForItem :

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    var height : CGFloat = 80

    let text = messagesDataArray[indexPath.row].messageText

    height = estimateFrameFor(text).height + 20

    return CGSize(width: collectionView.bounds.size.width, height: height)


}

private func estimateFrameFor(_ text : String) -> CGRect {
    let size = CGSize(width: 200, height: 1000)
    return NSString(string: text).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName : UIFont(name: "AvenirNext-Medium", size: 14)!], context: nil)
}

And the FlowLayout I'm using is this :

Github - jochenschoellig - ChatCollectionViewFlowLayout

Hey, I have created a small project with the problem: Github - testLP

1 Answers
Related