重新加载数据后,Self Sizing CollectionView 单元格无法正常工作

问题描述

我正在创建一个 collectionView,它可以有多个部分和一个自动调整大小的单元格。

它在初始布局上运行良好。但是重新加载后,我遇到了三个问题

  1. 滚动变得断断续续
  2. 单元格大小改变,单元格相互重叠
  3. 重新加载数据后内容偏移量发生变化

这是视图控制器代码

    class ViewController: UIViewController {

    @IBOutlet var collectionView: UICollectionView!
    var isLayout = false
     let dataSource: [Model] = [
        Model(title: "The domains wikipedia.com and wikipedia.org were registered on January 12,2001[21] and January 13,2001[22] respectively,and Wikipedia was launched on January 15,2001,[13] as a single English-language edition at www.wikipedia.com,[23] and announced by Sanger on the Nupedia mailing list.[17] Wikipedia's policy of [24] was codified in its first few months. Otherwise,there were relatively few rules initially and Wikipedia operated independently of Nupedia.[17] Originally,Bomis intended to make Wikipedia a business for profit.[25]"),Model(title: "Otherwise,Model(title: "of Nupedia.[17] Originally,Model(title: "The domains wikipedia.com and wikipedia.org were registered on January 12,"),Model(title: "as a single English-language edition at www.wikipedia.com,[23] and announced by Sanger on the Nupedia mailing list.[17] "),Model(title: " 2001,there were relatively few rules initially and Wikipedia operated independently"),Model(title: "2001[21] and January 13,[23] and announced by Sanger on the Nupedia mailing list.[17] Wikipedia's policy of [24] was codified in "),Model(title: "y 13,there were relatively few rules initially and Wikipedia operated indepe"),Model(title: "domains wikipedia.com and wikipedia.org were registered on January 12,[13] as a singl"),Model(title: "s first few months. Otherwise,there were relatively few rules initially and Wikipedia operated independently "),Model(title: "i as a single English-language edition at www.wikipedia.com,[23] and announced by Sanger on the "),Model(title: "January 13,Model(title: "Eespectively,[23] and announced by Sanger on the Nupedia m"),Model(title: "anguage edition at www.wikipedia.com,Model(title: "anguage edition at www.wiki"),Model(title: "domains"),Model(title: "domains")
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if !isLayout {
            if  let collectionViewLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
                  collectionViewLayout.estimatedItemSize =  CGSize(width: collectionView.frame.width/2,height: 100)
                  collectionViewLayout.invalidateLayout()
              }
            isLayout = true
        }

    }

    @IBAction func reloadData(sender: UIButton) {
        collectionView.reloadData()
    }
}

// MARK: - Collection view delegate and data source methods

extension ViewController: UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {

    func collectionView(_ collectionView: UICollectionView,numberOfItemsInSection section: Int) -> Int {
        return dataSource.count
    }

    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 6
    }

    func collectionView(_ collectionView: UICollectionView,cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier:"cell",for: indexPath) as! TestCollectionViewCell

        cell.titleLabel.text = dataSource[indexPath.item].title
        cell.layer.borderWidth = 1
        cell.layer.borderColor = UIColor.lightGray.cgColor
        if indexPath.section % 2 == 0 {
            cell.maxWidth = collectionView.bounds.width/2
        } else {
            cell.maxWidth = collectionView.bounds.width
        }

        return cell
    }
}

下面是collectionView单元格代码

导入 UIKit

class TestCollectionViewCell: UICollectionViewCell {

    @IBOutlet var titleLabel: UILabel!

    @IBOutlet private var maxWidthConstraint: NSLayoutConstraint! {
        didSet {
            maxWidthConstraint.isActive = false
        }
    }

    var maxWidth: CGFloat? = nil {
        didSet {
            guard let maxWidth = maxWidth else {
                return
            }
            maxWidthConstraint.isActive = true
            maxWidthConstraint.constant = maxWidth
        }
    }

    override func awakeFromNib() {
        super.awakeFromNib()

        contentView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            contentView.leftAnchor.constraint(equalTo: leftAnchor),contentView.rightAnchor.constraint(equalTo: rightAnchor),contentView.topAnchor.constraint(equalTo: topAnchor),contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
            ])
    }
}

CollectionView 单元格只包含一个标签

  1. label(leading,trailing,top,bottom) 等于 collectionview 单元格(前导、尾随、顶部、底部
  2. 标签宽度等于 50 和 900 优先权

enter image description here

这是演示源代码 https://drive.google.com/file/d/1C_LhIsTPRSW19UKMQPJiroAZ8KO4gciW/view

解决方法

  1. 正确设置您的收藏视图:
 let layout = UICollectionViewFlowLayout()
 layout.scrollDirection = .vertical //Specify the scroll direction
 collectionView.setCollectionViewLayout(layout,animated: true)
 collectionView.delaysContentTouches = false
  1. 在每个部分之间设置您想要的空间:
 func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout,insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 0,left: 5,bottom: 0,right: 0)
    }
  1. 在单元格之间设置一些空间:
 func collectionView(_ collectionView: UICollectionView,minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 5
    }
  1. 为每个单元格设置自定义宽度和高度。我们将使用两个辅助函数来实现您的需求:
 func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout:
                            UICollectionViewLayout,sizeForItemAt indexPath: IndexPath) -> CGSize {
        return calculateWidth(inString: dataSource[indexPath.row].title)+30,height: calculateHeight(inString: dataSource[indexPath.row].title)+30 )

//Calling these functions will return the dimension of each element in your array 
//Here I add 30 points because that's what I liked it better. If you don't like it,delete that part.
    }
    

这是您将用于计算将放置在每个集合视图单元格内的文本宽度的函数。它返回它的宽度。

    func calculateWidth(inString:String) -> CGFloat{
        let messageString = inString
        let attributes : [NSAttributedString.Key : Any] = [NSAttributedString.Key(rawValue:
                                                                                    NSAttributedString.Key.font.rawValue) : UIFont.systemFont(ofSize:
                                                                                                                                                universalFont(size: 15))] //If you use a different font/dimension,change it here
        
        let attributedString : NSAttributedString = NSAttributedString(string: messageString,attributes: attributes)
        let rect : CGRect = attributedString.boundingRect(with: CGSize(width: 222.0,height: CGFloat.greatestFiniteMagnitude),options: .usesLineFragmentOrigin,context: nil)
        
        let requredSize:CGRect = rect
        return requredSize.width
    }

这是您将用于计算将放置在每个集合视图单元格内的文本高度的函数。它返回它的高度。

    func calculateHeight(inString:String) -> CGFloat{
        let messageString = inString
        let attributes : [NSAttributedString.Key : Any] = [NSAttributedString.Key(rawValue:
                                                                                    NSAttributedString.Key.font.rawValue) : UIFont.systemFont(ofSize:
                                                                                                                                                universalFont(size: 15))] //If you use a different font/dimension,context: nil)
        
        let requredSize:CGRect = rect
        return requredSize.height
    }
  1. 在你的 cellForItemAt 集合中:
cell.textLabel?.sizeToFit()
cell.textLabel?.numberOfLines = 0
,

也许尝试使用UICollectionViewFlowLayout.automaticSize

collectionViewLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
collectionViewLayout.invalidateLayout()

更改后,我在模拟器上看不到任何重叠