Unable to simultaneously satisfy constraints when animating constraint - ios

This is my code:
class AvailableTrainScene: UIView {
let seeMoreButton: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .blue
v.layer.cornerRadius = 20
v.setTitle("Voir Plus", for: .normal)
return v
}()
var seeMoreBottom: NSLayoutConstraint?
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(seeMoreButton)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
NSLayoutConstraint.activate([
seeMoreButton.heightAnchor.constraint(equalToConstant: 40),
seeMoreButton.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.8),
seeMoreButton.centerXAnchor.constraint(equalTo: centerXAnchor)
])
seeMoreBottom = seeMoreButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8)
seeMoreBottom?.isActive = false
}
func hideSeeMore(_ hide: Bool) {
if hide {
seeMoreBottom?.constant = 40
}else{
seeMoreBottom?.constant = -8
}
UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 4, options: .curveEaseOut, animations: {
self.layoutIfNeeded()
}, completion: nil)
}
}
i get this error when animating using the function hideSeeMore:
LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x28060dc70 UIButton:0x105534920'Voir Plus'.bottom == DzTrain_2_0.AvailableTrainScene:0x1055300d0.bottom - 8 (active)>",
"<NSLayoutConstraint:0x2806894a0 UIButton:0x105534920'Voir Plus'.bottom == DzTrain_2_0.AvailableTrainScene:0x1055300d0.bottom + 40 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x2806894a0 UIButton:0x105534920'Voir Plus'.bottom == DzTrain_2_0.AvailableTrainScene:0x1055300d0.bottom + 40 (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
i tried to add tow constraints one to hide and another to show (priority is deferent) then activate/deactivate them but nothing happend?
Someone have this before?

Put constraints in
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(seeMoreButton)
NSLayoutConstraint.activate([
seeMoreButton.heightAnchor.constraint(equalToConstant: 40),
seeMoreButton.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.8),
seeMoreButton.centerXAnchor.constraint(equalTo: centerXAnchor)
])
seeMoreBottom = seeMoreButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8)
seeMoreBottom?.isActive = false
}
as this
override func layoutSubviews()
is called for any
self.layoutIfNeeded()
which causes your edit in constant to conflict with a new overwrite here
seeMoreBottom = seeMoreButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8)
seeMoreBottom?.isActive = false

Related

Swift - animate linear diagram with minimal segment

I have a linear diagram view which basically is nothing but three views. They represent the percentage of certain asset in a portfolio. So, when I click a button, I call update(multipliers: [CGFloat]) and the animation starts. However, I ran into a few issues. Imagine if I have $10000 in stocks, $5000 in bonds and $1 in ETFs. The ETF view won't be visible at all because it's so small. But even in this case I want to show a 10px or something width view. How can I achieve this? Also, my debugger keeps warning me
Unable to simultaneously satisfy constraints. Probably at least one
of the constraints in the following list is one you don't want.
which is understandable, but I don't think is fixable because of multipliers and constants not matching the width.
So, how can I solve the issue?
import UIKit
class StripView: UIView {
var stocksConstraint = NSLayoutConstraint()
var bondsConstraint = NSLayoutConstraint()
var etfConstraint = NSLayoutConstraint()
let stocksView: UIView = {
let view = UIView()
view.backgroundColor = .lightGray
view.layer.cornerRadius = 4
return view
}()
let bondsView: UIView = {
let view = UIView()
view.backgroundColor = .lightGray
view.layer.cornerRadius = 4
return view
}()
let etfView: UIView = {
let view = UIView()
view.backgroundColor = .lightGray
view.layer.cornerRadius = 4
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear
layer.cornerRadius = 4
layer.masksToBounds = true
setupViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(multipliers: [CGFloat]) {
stocksView.removeConstraint(stocksConstraint)
stocksConstraint = stocksView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: multipliers[0])
stocksConstraint.isActive = true
bondsView.removeConstraint(bondsConstraint)
bondsConstraint = bondsView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: multipliers[1])
bondsConstraint.isActive = true
etfView.removeConstraint(etfConstraint)
etfConstraint = etfView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: multipliers[2])
etfConstraint.isActive = true
UIView.animate(withDuration: 1.0) {
self.layoutIfNeeded()
self.stocksView.backgroundColor = .systemRed
self.bondsView.backgroundColor = .systemGreen
self.etfView.backgroundColor = .systemBlue
}
}
func setupViews() {
let views = [stocksView, bondsView, etfView]
views.forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
addSubview($0)
}
stocksConstraint = stocksView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.33)
bondsConstraint = bondsView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.33)
etfConstraint = etfView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.33)
NSLayoutConstraint.activate([
stocksView.topAnchor.constraint(equalTo: topAnchor),
stocksView.bottomAnchor.constraint(equalTo: bottomAnchor),
stocksView.leadingAnchor.constraint(equalTo: leadingAnchor),
stocksConstraint,
bondsView.topAnchor.constraint(equalTo: topAnchor),
bondsView.bottomAnchor.constraint(equalTo: bottomAnchor),
bondsView.leadingAnchor.constraint(equalTo: stocksView.trailingAnchor, constant: 2),
bondsConstraint,
etfView.topAnchor.constraint(equalTo: topAnchor),
etfView.bottomAnchor.constraint(equalTo: bottomAnchor),
etfView.leadingAnchor.constraint(equalTo: bondsView.trailingAnchor, constant: 2),
etfConstraint
])
}
}

Swift - Autolayout constraints UIView-Encapsulated-Layout-Width

I'm trying to do UI programatically and I'm getting weird constraint errors, but when I run the app, it it looks as expected.
What I'm trying to do:
I have a ViewController TodayVC where I have a UIView and I'm trying to render content of MWStepsActivityVC in that view.
Here is my TodayVC:
class TodayVC: UIViewController {
let scrollView = UIScrollView()
let contentView = UIView()
let stepsActivityView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
configureViewController()
configureScrollView()
configureContainerViews()
layoutUI()
}
func configureViewController() {
view.backgroundColor = .systemBackground
}
func configureScrollView() {
view.addSubview(scrollView)
scrollView.addSubview(contentView)
scrollView.pinToEdges(of: view)
contentView.pinToEdges(of: scrollView)
NSLayoutConstraint.activate([
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
contentView.heightAnchor.constraint(equalToConstant: 600)
])
}
func configureContainerViews() {
self.add(childVC: MWStepsActivityVC(activityType: .steps), to: self.stepsActivityView)
}
func layoutUI() {
contentView.addSubview(stepsActivityView)
stepsActivityView.translatesAutoresizingMaskIntoConstraints = false
let padding: CGFloat = 20
let itemHeight: CGFloat = 140
NSLayoutConstraint.activate([
stepsActivityView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: padding),
stepsActivityView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -padding),
stepsActivityView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding),
stepsActivityView.heightAnchor.constraint(equalToConstant: itemHeight),
])
print("TodayVC view width \(view.frame.size.width)")
}
func add(childVC: UIViewController, to containerView: UIView) {
addChild(childVC)
containerView.addSubview(childVC.view)
childVC.view.frame = containerView.bounds
childVC.didMove(toParent: self)
}
}
UIViewExtension:
extension UIView {
func addSubviews(_ views: UIView...) {
for view in views {
addSubview(view)
}
}
func pinToEdges(of superview: UIView) {
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
topAnchor.constraint(equalTo: superview.topAnchor),
leadingAnchor.constraint(equalTo: superview.leadingAnchor),
trailingAnchor.constraint(equalTo: superview.trailingAnchor),
bottomAnchor.constraint(equalTo: superview.bottomAnchor),
])
}
}
MWStepsActivityVC:
class MWActivityVC: UIViewController {
let iconImageView = UIImageView()
let titleLabel = UILabel()
let counterLabel = UILabel()
init(activityType: ActivityType) {
super.init(nibName: nil, bundle: nil)
self.set(activityType: activityType)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
configureBackgroundView()
layoutUI()
placeholderData()
}
fileprivate func placeholderData() {
iconImageView.image = SFSymbols.steps
titleLabel.text = "StepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsSteps"
counterLabel.text = "9000"
counterLabel.backgroundColor = .red
}
fileprivate func configureBackgroundView() {
view.layer.cornerRadius = 18
}
fileprivate func layoutUI() {
view.addSubviews(iconImageView, titleLabel, counterLabel)
iconImageView.translatesAutoresizingMaskIntoConstraints = false
titleLabel.translatesAutoresizingMaskIntoConstraints = false
counterLabel.translatesAutoresizingMaskIntoConstraints = false
let padding: CGFloat = 20
NSLayoutConstraint.activate([
iconImageView.topAnchor.constraint(equalTo: view.topAnchor, constant: padding),
iconImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding),
iconImageView.heightAnchor.constraint(equalToConstant: 20),
iconImageView.widthAnchor.constraint(equalToConstant: 20),
titleLabel.centerYAnchor.constraint(equalTo: iconImageView.centerYAnchor),
titleLabel.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: padding),
titleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -padding),
titleLabel.heightAnchor.constraint(equalToConstant: 20),
counterLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -padding),
counterLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding),
counterLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -padding),
counterLabel.heightAnchor.constraint(equalToConstant: 40)
])
print("MWStepsActivityVC view width \(view.frame.size.width)")
}
}
I'm getting these errors:
"<NSLayoutConstraint:0x600002228320 H:|-(20)-[UIImageView:0x7fc0abe077a0] (active, names: '|':UIView:0x7fc0abd10850 )>",
"<NSLayoutConstraint:0x6000022283c0 UIImageView:0x7fc0abe077a0.width == 20 (active)>",
"<NSLayoutConstraint:0x600002228460 H:[UIImageView:0x7fc0abe077a0]-(20)-[UILabel:0x7fc0abe08a70] (active)>",
"<NSLayoutConstraint:0x6000022284b0 UILabel:0x7fc0abe08a70.trailing == UIView:0x7fc0abd10850.trailing - 20 (active)>",
"<NSLayoutConstraint:0x600002230b90 'UIView-Encapsulated-Layout-Width' UIView:0x7fc0abd10850.width == 0 (active)>",
"<NSLayoutConstraint:0x600002228320 H:|-(20)-[UIImageView:0x7fc0abe077a0] (active, names: '|':UIView:0x7fc0abd10850 )>",
"<NSLayoutConstraint:0x600002228460 H:[UIImageView:0x7fc0abe077a0]-(20)-[UILabel:0x7fc0abe08a70] (active)>",
"<NSLayoutConstraint:0x6000022284b0 UILabel:0x7fc0abe08a70.trailing == UIView:0x7fc0abd10850.trailing - 20 (active)>",
"<NSLayoutConstraint:0x600002230b90 'UIView-Encapsulated-Layout-Width' UIView:0x7fc0abd10850.width == 0 (active)>",
"<NSLayoutConstraint:0x6000022285a0 H:|-(20)-[UILabel:0x7fc0abe08ce0] (active, names: '|':UIView:0x7fc0abd10850 )>",
"<NSLayoutConstraint:0x6000022285f0 UILabel:0x7fc0abe08ce0.trailing == UIView:0x7fc0abd10850.trailing - 20 (active)>",
"<NSLayoutConstraint:0x600002230b90 'UIView-Encapsulated-Layout-Width' UIView:0x7fc0abd10850.width == 0 (active)>"
I tried to use the wtfautolayout.com website to understand what's wrong, and it seems the issue is with UIView-Encapsulated-Layout-Width it thinks it's set to 0, but when I try to print view.frame.size.width I get correct width:
MWStepsActivityVC view width 375.0
TodayVC view width 375.0
Can someone help me with it? I'm struggling with this for a day already.
Finally I was able to fix it. I forgot to run configureContainerViews on main thread...
DispatchQueue.main.async {
self.configureContainerViews()
}

Unable to simultaneously satisfy constraints when adding any non-zero constraint programmatically

import UIKit
#IBDesignable
class LargeButtonWithIcon: UIView {
var iconBackgroundView: UIView?
var iconIv: UIImageView?
#IBInspectable var iconImage: UIImage? {
didSet {
self.iconIv?.image = iconImage
}
}
override init(frame: CGRect) {
super.init(frame: frame)
initializeView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
initializeView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
initializeView()
}
private func initializeView() {
initializeIconView()
}
private func initializeIconView() {
iconBackgroundView = UIView(frame: CGRect(x: 0, y: 0, width: self.frame.height, height: self.frame.height))
addSubview(iconBackgroundView!)
iconBackgroundView?.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
iconBackgroundView?.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
iconBackgroundView?.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
iconIv = UIImageView()
iconIv?.image = iconImage
iconIv?.clipsToBounds = true
iconIv?.contentMode = .scaleAspectFit
iconIv?.translatesAutoresizingMaskIntoConstraints = false
iconBackgroundView!.addSubview(iconIv!)
iconBackgroundView?.leadingAnchor.constraint(equalTo: iconBackgroundView!.leadingAnchor, constant: 20).isActive = true
// iconBackgroundView?.topAnchor.constraint(equalTo: iconBackgroundView!.topAnchor, constant: 0).isActive = true
// iconBackgroundView?.rightAnchor.constraint(equalTo: iconBackgroundView!.rightAnchor, constant: 20).isActive = true
// iconBackgroundView?.bottomAnchor.constraint(equalTo: iconBackgroundView!.bottomAnchor, constant: 20).isActive = true
// iconBackgroundView?.centerXAnchor.constraint(equalTo: iconBackgroundView!.centerXAnchor).isActive = true
// iconBackgroundView?.centerYAnchor.constraint(equalTo: iconBackgroundView!.centerYAnchor).isActive = true
}
}
I'm trying to achieve the following:
iconBackgroundView is the square at the left of the button, iconIv is the icon in that square.
When I activate any non-zero constraint for the iconIv (or all of them), it prints the warning:
2020-06-03 01:26:37.163392+0300 FindDifferences[5310:34823037] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x6000012bc6e0 UIView:0x7f880c0075a0.leading == UIView:0x7f880c0075a0.leading + 20 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x6000012bc6e0 UIView:0x7f880c0075a0.leading == UIView:0x7f880c0075a0.leading + 20 (active)>
But if I change the its constant to 0, it does not. Why, and how should I center the iconIv in this view with given padding?
This line doesn't make sense because you're trying to set a view's leading anchor equal to its own leading anchor.
iconBackgroundView?.leadingAnchor.constraint(equalTo: iconBackgroundView!.leadingAnchor, constant: 20).isActive = true
I think what you actually want is this:
iconIv?.leadingAnchor.constraint(equalTo: iconBackgroundView!.leadingAnchor, constant: 20).isActive = true

UIView internal constraints

I have created a simple UIView that contains a red box (UIImage) at its centre. When all the constraints are constants the code works fine. However, if I replace the height constraint with one that makes the box half the height of the view then the box disappears.
I assume that this is either because I am doing it wrong (obviously) or I need to do something more to force the constraint to realise the UIView height is greater than zero.
How do I set the redBox height constraint so that it is always half the height of the BoxView?
import UIKit
class BoxView: UIView {
public var redBox: UIImageView
public override init(frame: CGRect) {
redBox = UIImageView(frame: .zero)
redBox.backgroundColor = .red
super.init(frame: frame)
self.backgroundColor = .yellow
addSubview(redBox)
redBox.translatesAutoresizingMaskIntoConstraints = false
let margins = layoutMarginsGuide
redBox.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
redBox.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
redBox.widthAnchor.constraint(equalToConstant: 100).isActive = true
//redBox.heightAnchor.constraint(equalToConstant: 100).isActive = true
redBox.heightAnchor.constraint(equalTo: self.heightAnchor, constant: 0.5)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view = BoxView()
}
}
Replace
redBox.heightAnchor.constraint(equalTo: self.heightAnchor, constant: 0.5)
with
redBox.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 0.5).isActive = true
NSLayoutConstraint.activate([
redBox.centerXAnchor.constraint(equalTo: self.centerXAnchor),
redBox.centerYAnchor.constraint(equalTo: self.centerYAnchor),
redBox.widthAnchor.constraint(equalToConstant: 100),
redBox.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 0.5)
])
In your current code first you miss the .isActive = true which has the same effect as if the line doesn't exist , and if specified this will make the box height equal to the view's height + constant ( = 0.5 )
box height = view height * multiplier + constant
and since default multiplier = 1 and you set constant = 0.5 this will be
box height = view height * 1.0 + 0.5
But instead you need
box height = view height * 0.5 + 0 // omit consatnt in constraint and it will be zero
class BoxView: UIView {
public var redBox: UIImageView
public override init(frame: CGRect) {
super.init(frame: frame)
redBox = UIImageView(frame: .zero)
redBox.backgroundColor = .red
self.backgroundColor = .yellow
addSubview(redBox)
redBox.translatesAutoresizingMaskIntoConstraints = false
let margins = layoutMarginsGuide
NSLayoutConstraint.activate([
redBox.centerXAnchor.constraint(equalTo: self.centerXAnchor),
redBox.centerYAnchor.constraint(equalTo: self.centerYAnchor),
redBox.widthAnchor.constraint(equalToConstant: 100),
redBox.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 0.5)
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

How to position a subView below a subView with dynamic height?

I have the following UIView:
It is a UIView which contains three subviews. A regular UILabel (Hello World) at the top, a custom UIViewController (CategoryList) which contains a CollectionView with buttons (alpha,beta, ...) and another custom UIViewController with just a label (SALUT).
I do auto-layout programmatically and position SALUT (var sCtrl) below the CategoryList (var catList) with
sCtrl.view.topAnchor.constraint(equalTo: catList.view.bottomAnchor).isActive = true
This results in the picture above, where SALUT is not positioned below the category list as I would like it to be. I sort of understand why since when I set the constraint the buttons are not yet laid out properly in the CollectionView and thus the bottom anchor of the catList is not set.
In the CategoryList:UIVIewController I have the following in order to get the collection view to get the correct height.
override public func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
heightConstraint = collectionView.heightAnchor.constraint(equalToConstant: collectionView.collectionViewLayout.collectionViewContentSize.height)
self.view.addConstraint(heightConstraint)
}
This works but since viewDidLayoutSubviews() is called after the constraint is set on SALUT the SALUT position is wrong. How can I get it right in an easy way? (I guess I could make a controller for my main view and check there when subviews are laid out and then update subview positions but this seems overly complicated to me).
The full code for the main view is below so you can see the layout positioning code. If needed I can also provide the subviews code but I suppose it shouldn't be needed since they should be considered black boxes...
class MyView: UIView {
let label = UILabel()
var sCtrl : SubController
var catList : CategoryList
var topConstraint = NSLayoutConstraint()
override init(frame: CGRect) {
sCtrl = SubController()
catList = CategoryList()
super.init(frame: frame)
setup()
}
private func setup() {
self.backgroundColor = UIColor.green
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = UIColor.yellow
label.text = "Hello world"
label.textAlignment = .center
self.addSubview(label)
catList.view.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(catList.view)
sCtrl.view.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(sCtrl.view)
let margins = self.layoutMarginsGuide
label.leadingAnchor.constraint(equalTo: margins.leadingAnchor, constant: 0).isActive = true
label.trailingAnchor.constraint(equalTo: margins.trailingAnchor, constant: 0).isActive = true
label.topAnchor.constraint(equalTo: margins.topAnchor, constant: 0).isActive = true
label.heightAnchor.constraint(equalToConstant: 100.0).isActive=true
catList.view.leadingAnchor.constraint(equalTo: margins.leadingAnchor, constant: 0).isActive = true
catList.view.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 1.0).isActive = true
catList.view.widthAnchor.constraint(equalTo: margins.widthAnchor, multiplier: 1.0).isActive = true
catList.view.trailingAnchor.constraint(equalTo: margins.trailingAnchor, constant:0).isActive = true
sCtrl.view.topAnchor.constraint(equalTo: catList.view.bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
let v = MyView(frame: CGRect(x: 0, y: 0, width: 400, height: 600))
Ok, I found the issue. I had missed to add the height constraint to the CategoryList's own view. Adding this it works just fine.
override public func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
heightConstraint = collectionView.heightAnchor.constraint(equalToConstant: collectionView.collectionViewLayout.collectionViewContentSize.height)
self.view.addConstraint(heightConstraint)
//THE MISSING LINE!!
self.view.heightAnchor.constraint(equalTo: self.collectionView.heightAnchor, constant: 0).isActive = true
}

Resources