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!
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 :
private var contentSizeObservation: NSKeyValueObservation?
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
})
deinit
call -deinit {
contentSizeObservation?.invalidate()
}
This approach will make sure that your collectionView is always as big in height as it's content.