0
votes

I am creating a collectionView that can have multiple sections with a self-sizing cell.

it works nicely on the initial layout. but after reload, I encounter three problem

  1. Scrolling become choppy
  2. cell size changed and cell overlap with each other
  3. content offset changed after reload data

here is ViewController code

    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, 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: "of Nupedia.[17] Originally, Bomis intended to make Wikipedia a business for profit.[25]"),
        Model(title: "The domains wikipedia.com and wikipedia.org were registered on January 12, 2001[21] and January 13,"),
        Model(title: "The domains wikipedia.com and wikipedia.org were registered on January 12, 2001[21] and January 13,"),
        Model(title: "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: "as a single English-language edition at www.wikipedia.com,[23] and announced by Sanger on the Nupedia mailing list.[17] "),
        Model(title: " 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"),
        Model(title: "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 "),
        Model(title: "y 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 indepe"),
        Model(title: "of Nupedia.[17] Originally, Bomis intended to make Wikipedia a business for profit.[25]"),
        Model(title: "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 singl"),
        Model(title: "s first few months. Otherwise, there were relatively few rules initially and Wikipedia operated independently "),
        Model(title: "s first few months. Otherwise,"),
        Model(title: "i as a single English-language edition at www.wikipedia.com,[23] and announced by Sanger on the "),
        Model(title: "January 13, 2001[22] respectively, and Wikipedia was launched on January 15, 2001,[13] as a singl"),
        Model(title: "Eespectively, 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 m"),
        Model(title: "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 singl"),
        Model(title: "anguage edition at www.wikipedia.com,[23] and announced by Sanger on the Nupedia m"),
        Model(title: "anguage edition at www.wiki"),
        Model(title: "domains"),
        Model(title: "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 singl"),
        Model(title: "Eespectively, 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 m"),
        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
    }
}

below is the collectionView cell code

import 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 cell contains only one label

  1. label(leading,trailing,top,bottom) equal to collectionview cell(leading,trailing,top,bottom)
  2. label width equal to 50 with 900 priority

enter image description here

Here is the demo source code https://drive.google.com/file/d/1C_LhIsTPRSW19UKMQPJiroAZ8KO4gciW/view

2
Do you need the cell's dimension to be based on the length of the text?Federica Benacquista
yes, I want cell height dynamic. it already worked on the given demo. but after reloading collectionview some problems occurred That I already mention in the Question.Bhavesh.iosDev

2 Answers

1
votes
  1. Properly set your collection view:
 let layout = UICollectionViewFlowLayout()
 layout.scrollDirection = .vertical //Specify the scroll direction
 collectionView.setCollectionViewLayout(layout, animated: true)
 collectionView.delaysContentTouches = false
  1. Set the space you want tone between each section:
 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 0)
    }
  1. Set some space between the cells:
 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 5
    }
  1. Set a custom width and height for each of your cell. We'll use two helper functions to achieve what you need:
 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.
    }
    

This is the function you'll use to calculate the width of the text you'll put inside of each collection view cell. It returns its width.

    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
    }

This is the function you'll use to calculate the height of the text you'll put inside of each collection view cell. It returns its height.

    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, 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.height
    }
  1. In your cellForItemAt set:
cell.textLabel?.sizeToFit()
cell.textLabel?.numberOfLines = 0
0
votes

Maybe try to use UICollectionViewFlowLayout.automaticSize:

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

After that change, I don't see any overlapping on the simulator