0
votes

I have this custom UIScrollView that I've constrained to the superview and it contains a UIView called 'contentView' which I pin to all edges of the UIScrollView. I then add a UIView that contains 2 UIViews and a UICollectionView to this 'contentView' which is found within the custom UIScrollView's class. From here I start setting constraints as normal. All this is done programmatically.

The 2 UIViews show correctly, but the collectionView does not. It doesn't even show. I'm setting the collectionView's constraints to all edges of the 'contentView'. I'm not setting the height as I want it to be based on the contentSize of the collectionView.

Here is the custom UIScrollView class:

import UIKit
import PureLayout
class ContentScrollView: UIScrollView {
    enum Direction {
        case vertical
        case horizontal
    }
    lazy var contentView: UIView = {
        let view = UIView()
        return view
    }()
    let direction: Direction
    private var didSetupConstraints = false
    // MARK: - Init
    override init(frame: CGRect) {
        self.direction = .vertical
        super.init(frame: frame)
        clipsToBounds = true
        self.addSubview(contentView)
    }
    init(direction: Direction) {
        self.direction = direction
        super.init(frame: CGRect.zero)
        clipsToBounds = true
        translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(contentView)
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func updateConstraints() {
        if !didSetupConstraints {
            contentView.autoPinEdgesToSuperviewEdges()
            switch direction {
            case .vertical:
                contentView.autoMatch(.width, to: .width, of: self)
                contentView.autoSetDimension(ALDimension.height,
                                             toSize: .leastNonzeroMagnitude,
                                             relation: .greaterThanOrEqual)
                break
            case .horizontal:
                contentView.autoMatch(.height, to: .height, of: self)
                contentView.autoSetDimension(ALDimension.width,
                                             toSize: .leastNonzeroMagnitude,
                                             relation: .greaterThanOrEqual)
            }
            didSetupConstraints = true
        }
        super.updateConstraints()
    }
}

Here are the constraints I've got:

private func configureMainScrollView() {
        scrollView.backgroundColor = .clear
        scrollView.delegate = self
        scrollView.alwaysBounceVertical = true
        scrollView.contentInset = UIEdgeInsets(top: 78, left: 0, bottom: 0, right: 0)
        
        view.addSubview(scrollView)
        
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.topAnchor.constraint(equalTo: customSpacer.bottomAnchor).isActive = true
        scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: customAdContainerView.topAnchor).isActive = true
        
        scrollView.contentView.addSubview(customContainerView)
        configureCustomView()
        
        scrollView.contentView.addSubview(collectionView)
        configureCollectionView()
    }
    
    private func configureCustomizeButtonView() {
        customContainerView.backgroundColor = .clear
        
        customButton.setTitle("Customize", for: .normal)
        customButton.backgroundColor = .clear
        customButton.setTitleColor(UIColor.appColorSelectedTab(), for: .normal)
        customButton.titleLabel?.tintColor = UIColor.appColorSelectedTab()
        customButton.titleLabel?.font = UIFont.appFontButtonTitle()
        customButton.contentHorizontalAlignment = .right;
        
        divider.backgroundColor = UIColor.appColorDividerViewBackground()
        
        customContainerView.translatesAutoresizingMaskIntoConstraints = false
        customContainerView.topAnchor.constraint(equalTo: scrollView.contentView.topAnchor).isActive = true
        customContainerView.leftAnchor.constraint(equalTo: scrollView.contentView.leftAnchor).isActive = true
        customContainerView.rightAnchor.constraint(equalTo: scrollView.contentView.rightAnchor).isActive = true
        
        customContainerView.addSubview(customButton)
        customContainerView.addSubview(divider)
        
        customButton.translatesAutoresizingMaskIntoConstraints = false
        customButton.topAnchor.constraint(equalTo: customContainerView.topAnchor, constant: 13).isActive = true
        customButton.rightAnchor.constraint(equalTo: customContainerView.rightAnchor, constant: -16).isActive = true
        customButton.leftAnchor.constraint(equalTo: customContainerView.leftAnchor).isActive = true
        customButton.heightAnchor.constraint(equalToConstant: 22).isActive = true
        
        divider.translatesAutoresizingMaskIntoConstraints = false
        divider.topAnchor.constraint(equalTo: customButton.bottomAnchor, constant: 16).isActive = true
        divider.leftAnchor.constraint(equalTo: customContainerView.leftAnchor, constant: 16).isActive = true
        divider.rightAnchor.constraint(equalTo: customContainerView.rightAnchor, constant: -16).isActive = true
        divider.bottomAnchor.constraint(equalTo: customizeButtonContainerView.bottomAnchor).isActive = true
        divider.heightAnchor.constraint(equalToConstant: 1).isActive = true
    }
    
    private func configureCollectionView() {
        collectionView.backgroundColor = .clear
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.isScrollEnabled = false
        
        collectionView.register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: customCellId)
        collectionView.register(CustomHeaderCollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: customHeaderId)
        
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.topAnchor.constraint(equalTo: customContainerView.bottomAnchor).isActive = true
        collectionView.leftAnchor.constraint(equalTo: scrollView.contentView.leftAnchor).isActive = true
        collectionView.rightAnchor.constraint(equalTo: scrollView.contentView.rightAnchor).isActive = true
        collectionView.bottomAnchor.constraint(equalTo: scrollView.contentView.bottomAnchor).isActive = true
        //collectionView.heightAnchor.constraint(equalToConstant: 750).isActive = true
    }

I'm wondering if it's the 'contentView' within the UIScrollView that is causing this somehow. You'll notice the commented out code that sets the height constraint for the collectionView. This is the only way it would show but obviously I'm looking for it to show the contentView size of the collectionView.

Any help with this would be appreciated. Thanks!

1
Where do the things you call customContainerView and collectionView come from?El Tomato
It's probably obvious to yourself as to under what class you are writing configureMainScrollView and other functions. What's obvious to you isn't to others.El Tomato
The configure methods are in my view controller. I'm declaring these UI elements at the top of the view controller and setting up the constraints in these methods that get called in ViewDidLoad. @ElTomatoSwiftCity

1 Answers

0
votes

The UICollectionView needs a height constraint (the same way any UIScrollView does).

Don't get confused between the frame.height and contentSize.height.

Say a UIScrollView has a height of 200 and content height is 1000. It knows that it needs to scroll to show all the content inside that 200 height.

If you don't set frame.height to 200, no matter what contentSize.height is - it won't show anything.

The same applies to this case. UICollectionView needs a height constraint even though you don't want it to scroll.

What you want here is frame.height == contentSize.height.

Try following :

  1. Declare a variable inside the class like this.
private var contentSizeObservation: NSKeyValueObservation?
  1. Add height constraint to collectionView, and start observing contentSize changes.
let cvHeight = collectionView.heightAnchor.constraint(equalToConstant: 0)
cvHeight.isActive = true

contentSizeObservation = collectionView.observe(\.contentSize, options: .new, changeHandler: { (cv, _) in
    cvHeight.constant = cv.collectionViewLayout.collectionViewContentSize.height
})
  1. Do NOT Forget to clean this up in deinit call -
deinit {
    contentSizeObservation?.invalidate()
}

This approach will make sure that your collectionView is always as big in height as it's content.