UICollectionView单元格在滚动时重新计算大小(闪烁)

时间:2017-05-23 12:47:08

标签: swift uicollectionview uicollectionviewlayout

初始表加载后,一切正常。但是在插入新单元格并快速向上滚动后,您可以看到一些单元格重新计算其大小(动画)。这真的很奇怪,最多2-3个单元。我使用了带有反转单元格的自下而上的集合视图和自定义流程布局,但它也没有动画。此外,当滚动时,键盘隐藏,所以它可能与它有关..

细胞插入:

  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)

问题视频:

this

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
    }

}

另外。 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)
}

我使用的FlowLayout是这样的:

the documentation of AdvertiseCallback class

嘿,我创建了一个带有问题的小项目: http://vimple.co/3c39cb325b9c4ec19173fea015b6cc8b

1 个答案:

答案 0 :(得分:2)

无法发表评论,没有足够的代表。但正如评论的那样,请提供ConversationLabel课程,sizeForItemAtIndexPath覆盖以及自定义FlowLayout课程的代码。添加自定义布局类时,很容易忽略某些结果行为。

如果没有给出这些方法,你在这里就不会有太多运气了。当你这样做时,我会查看并更新我的答案。

但是,从视频来看,这是我首先要看的内容:

由于当索引变得可见时,单元格会重新添加到collectionview的可见部分,因此动画可能是由于您在sizeForItemAtIndexPath内部应用的大小而可能是如果用于应用大小的任何单元格元素没有足够快地更新,则会延迟。或者由于滞后,动画以某种方式可见,因为每次将单元视图带入视图时都会调用约束重置器(这可能会导致一些滞后,具体取决于设备)。 由于你的apply(_ layoutAttributes: UICollectionViewLayoutAttributes)覆盖,我建议使用后者。给出文档here:您正在调用updateConstraints()一次,然后立即使用setNeedsUpdateConstraints(),当单元格视图变为可见时,会调度updateConstraints()调用单元格视图的布局(当你看到动画时)。所以你在这里做了两次工作,这可能会添加到动画队列中。或者,由于您还要重置transform属性,因此您可能还在安排一个您可能没有考虑过的额外动画。 最后,虽然这可能暗示了这种行为源于何处,但如果没有自定义的FlowLayout类,就不可能说出来。如上所述here

  

如果您继承并实现任何自定义布局属性,则必须   还会覆盖继承的isEqual:方法来比较值   你的财产。在iOS 7及更高版本中,集合视图不会   如果这些属性未更改,则应用布局属性。它   通过比较旧属性来确定属性是否已更改   和使用isEqual:方法的新属性对象。

并且,正如本post about iOS7 and later os builds所述:  由于您覆盖了apply(_ layoutAttributes: UICollectionViewLayoutAttributes)方法,因此您还应该使用自己的代码覆盖isEqual(_ object: Any?) method,该代码会按您希望的方式比较布局属性。

isEqual覆盖的example

override public func isEqual(_ object: Any?) -> Bool {
        guard let rhs = object as? DateClass else {
            return false
        }
        let lhs = self

        return lhs.date1 == rhs.date1 &&
            lhs.date2 == rhs.date2 &&
            lhs.date3 == rhs.date3
    }

您将DateClass与UICollectionViewLayoutAttributes交换,将覆盖置于UICollectionViewLayoutAttributes子类的范围内。

因此,就我所说的内容而言,似乎可能会在您的代码中意外地过度调用某些方法。或者您可能没有正确地继承UICollectionViewLayoutAttributes。同样,这些是我提供的最佳解决方案。添加缺少的代码,我将更新答案!