iosswiftuicollectionviewuicollectionviewcelluicollectionviewlayout

Nested collection views with dynamic content


I am using two nested collection views. I have added the ChildCollectionView to the ParentCollectionViewCell, and ChildCollectionView have 3 cells in it but the ParentCollectionViewCell does not adjust the frame of the cell as per the content.

Here's the code,

ParentCollectionView

class ViewController: UIViewController {
    
    var parentCollectionView: UICollectionView = {
        let _collectionView = UICollectionView.init(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout.init())
        return _collectionView
    }()
    
    let id = "ParentCollectionViewCell"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        view.addSubview(parentCollectionView)
        
        parentCollectionView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            parentCollectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            parentCollectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            parentCollectionView.topAnchor.constraint(equalTo: view.topAnchor),
            parentCollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
        
        parentCollectionView.dataSource = self
        
        parentCollectionView.register(UINib(nibName: id, bundle: nil), forCellWithReuseIdentifier: id)
        
        if let flowLayout = parentCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
            flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
        }
    }

}

extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        1
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: id, for: indexPath) as! ParentCollectionViewCell
        
        cell.backgroundColor = .red
        
        return cell
        
    }
    
}

ParentCollectionViewCell

class ParentCollectionViewCell: UICollectionViewCell {

    var childCollectionView: UICollectionView = {
        let _collectionView = UICollectionView.init(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout.init())
        return _collectionView
    }()
    
    let reuseId = "ChildCollectionViewCell"
    
    private let data = ["ChildCell1","ChildCell2","ChildCell3"]

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        setupViews()
    }
    
    func setupViews() {
        contentView.addSubview(childCollectionView)

        childCollectionView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            childCollectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            childCollectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
            childCollectionView.topAnchor.constraint(equalTo: contentView.topAnchor),
            childCollectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
            childCollectionView.widthAnchor.constraint(equalToConstant: 360)
        ])

        childCollectionView.dataSource = self
        
        childCollectionView.register(UINib(nibName: reuseId, bundle: nil), forCellWithReuseIdentifier: reuseId)
        
        if let flowLayout = childCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
            flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
        }
    }
}

extension ParentCollectionViewCell: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        data.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseId, for: indexPath) as! ChildCollectionViewCell
        
        cell.backgroundColor = .gray
        
        cell.setupViews()
        
        return cell
    }
}

ChildCollectionViewCell

class ChildCollectionViewCell: UICollectionViewCell {
    

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }
    
    func setupViews() {
        
        let label = UILabel()
        
        label.text = "Child Collection"
        
        label.numberOfLines = 0
        
        label.font = label.font.withSize(50)
        
        contentView.addSubview(label)
        
        label.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
            label.topAnchor.constraint(equalTo: contentView.topAnchor),
            label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
        ])
        
    }

}

Current Output Current Output

Expected Output Expected Output


Solution

  • You should not complex this layouts, Instead you can use UIStackView to achieve the UI hierarchy that you want.

    You can use, UIStackView inside your UICollectionViewCell, this way your cell will not get collapsed and its height will remain consistent with you content.