【问题标题】:UICollectionView cells recalculating sizes (flicker) while scrollingUICollectionView 单元格在滚动时重新计算大小(闪烁)
【发布时间】:2017-05-23 12:47:08
【问题描述】:

初始表加载后,一切正常。但是在插入一个新单元格并快速向上滚动后,您可以看到一些单元格重新计算它们的大小(动画)。这真的很奇怪,最多发生 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)

问题视频:

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
    }

}

还有。对话单元用户:

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

    }
}

对话标签:

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 是这样的:

Github - jochenschoellig - ChatCollectionViewFlowLayout

嘿,我创建了一个有问题的小项目: Github - testLP

【问题讨论】:

  • 分享你的 cellForRowAtIndexPath 的代码。

标签: swift uicollectionview uicollectionviewlayout


【解决方案1】:

无法评论,没有足够的代表。但正如所评论的,请提供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
    }

你可以用你的 UICollectionViewLayoutAttributes 替换 DateClass,把覆盖放在你的 UICollectionViewLayoutAttributes 子类的范围内。

据我所知,您的代码中的某些方法似乎可能被意外过度调用。或者您可能没有正确地继承 UICollectionViewLayoutAttributes。同样,这些是我为您提供的最佳解决方案。添加缺少的代码,我会更新答案!

【讨论】:

  • 您好,谢谢您的回复!我添加了缺失的代码片段
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-20
  • 2014-07-08
  • 2012-06-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多