iosswiftuicollectionviewconstraintsstackview

How to set dynamic height for collectionView inside a StackView programmatically in Swift?


So I have a collection view inside a stackView, along with two UIViews, and the stackView is a subview of a scrollView. I want my collectionView to adjust its height dynamically and to not affect the other views in the stack view. I made a diagram showing what I am trying to achieve, so far... no success, the other two views keep getting stretched out or the collectionView doesn't seem to be exactly in the middle. Is there anyone that can help me? Please.

Constraints Code:

private func setupView() {
    view.addSubview(scrollView)
    scrollView.addSubview(mainStack)
    headerStackView.addArrangedSubview(titleLabel)
    footerStackView.addArrangedSubview(footerLabel)
    mainStack.addArrangedSubview(headerStackView)
    mainStack.addArrangedSubview(collectionView)
    mainStack.addArrangedSubview(footerStackView)

    let layoutGuide = view.safeAreaLayoutGuide

    NSLayoutConstraint.activate([
        collectionView.heightAnchor.constraint(equalToConstant: 500),

        scrollView.topAnchor.constraint(equalTo: layoutGuide.topAnchor),
        scrollView.bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor),
        scrollView.leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor, constant: 16),
        scrollView.trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor, constant: -16),

        mainStack.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
        mainStack.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 16),
        mainStack.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
        mainStack.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
        mainStack.bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor)
    ])
}

Collection View and StackView Code:

private lazy var collectionView: UICollectionView = {
    let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
    collectionView.translatesAutoresizingMaskIntoConstraints = false
    collectionView.backgroundColor = .white
    collectionView.register(CustomCell.self, forCellWithReuseIdentifier: CustomCell.reuseId)
    collectionView.delegate = self
    collectionView.dataSource = self
    collectionView.showsVerticalScrollIndicator = false
    collectionView.isScrollEnabled = false
    return collectionView
}()

private lazy var mainStack: UIStackView = {
    let stackView = UIStackView()
    stackView.translatesAutoresizingMaskIntoConstraints = false
    stackView.axis = .vertical
    stackView.spacing = 16
    stackView.distribution = .fill
    return stackView
}()

Delegate Function:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize {

        let collectionViewSize = collectionView.frame.size.width - 16
        let collectionViewHeightSize = collectionView.frame.size.height
        return CGSize(width: collectionViewSize/2, height: collectionViewHeightSize / 2)
    }

Here is my diagram:

enter image description here


Solution

  • This line is wrong:

    mainStack.bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor)
    

    that says "keep the mainStack bottom at the view's bottom, regardless of scrolling.

    it should be:

    mainStack.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
    

    now the mainStack becomes the "scrollable content" of your scroll view.