I have custom UICollectionView
class MyCollectionViewCell: UICollectionViewCell {
var v0: UIImageView = {
let iv = UIImageView()
iv.image = UIImage()
iv.contentMode = .scaleAspectFit
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
var v1: UILabel = {
let lb = UILabel()
lb.font = UIFont.systemFont(ofSize: 16, weight: .regular)
lb.textAlignment = .center
lb.backgroundColor = .clear
lb.translatesAutoresizingMaskIntoConstraints = false
return lb
}()
var v2: UILabel = {
let lb = UILabel()
lb.font = UIFont.systemFont(ofSize: 16, weight: .regular)
lb.textAlignment = .center
lb.backgroundColor = .clear
lb.clipsToBounds = true
lb.translatesAutoresizingMaskIntoConstraints = false
return lb
}()
override init(frame: CGRect) {
super.init(frame: frame)
addViews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
addViews()
}
private func addViews() {
let stack = UIStackView(arrangedSubviews: [v0, v1, v2])
stack.distribution = .equalSpacing
stack.alignment = .fill
stack.axis = .vertical
stack.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(stack)
NSLayoutConstraint.activate([
v0.heightAnchor.constraint(equalToConstant: 24),
v0.widthAnchor.constraint(equalTo: v0.heightAnchor, multiplier: 1.5),
v1.widthAnchor.constraint(equalTo: v1.heightAnchor),
v2.widthAnchor.constraint(equalTo: v2.heightAnchor),
stack.topAnchor.constraint(equalTo: self.topAnchor, constant: 0),
stack.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0),
stack.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0),
stack.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0)
])
}
}
I use this inside standard UICollectionView with UICollectionViewFlowLayout.
let layout = HorizontalLayout()
layout.minimumInteritemSpacing = 2
layout.minimumLineSpacing = 2
layout.scrollDirection = .horizontal
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
When the view is first shown, there is no warning. However, when I manually select a cell using
colView.reloadData()
colView.layoutIfNeeded()
colView.selectItem(at: IndexPath(row: activeIndex, section: 0),
animated: false,
scrollPosition: .centeredHorizontally)
I got a lot of constraints warnings from the cell that complains almost on every constraint.
For example:
[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:0x2833edc20 UIImageView:0x12a06e580.height == 24 (active)>",
"<NSLayoutConstraint:0x2833edc70 UIImageView:0x12a06e580.width == 1.5*UIImageView:0x12a06e580.height (active)>",
"<NSLayoutConstraint:0x2833eddb0 H:|-(0)-[UIStackView:0x12a06ed20] (active, names: '|':app.MyCollectionViewCell:0x12a06e310 )>",
"<NSLayoutConstraint:0x2833ede00 UIStackView:0x12a06ed20.trailing == app.MyCollectionViewCell:0x12a06e310.trailing (active)>",
"<NSLayoutConstraint:0x2833ee670 'UISV-canvas-connection' UIStackView:0x12a06ed20.leading == UIImageView:0x12a06e580.leading (active)>",
"<NSLayoutConstraint:0x2833ee490 'UISV-canvas-connection' H:[UIImageView:0x12a06e580]-(0)-| (active, names: '|':UIStackView:0x12a06ed20 )>",
"<NSLayoutConstraint:0x2833d4b90 'UIView-Encapsulated-Layout-Width' app.MyCollectionViewCell:0x12a06e310.width == 50 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x2833edc20 UIImageView:0x12a06e580.height == 24 (active)>
What is wrong and why the problem appears only after manual selection?
First, when using a UICollectionViewCell
(and when using a UITableViewCell
), all subviews should be added to the cell's contentView
:
//self.addSubview(stack)
contentView.addSubview(stack)
and:
//stack.topAnchor.constraint(equalTo: self.topAnchor, constant: 0),
//stack.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0),
//stack.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0),
//stack.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0)
stack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0),
stack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0),
stack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0),
stack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0)
Next, to avoid the auto layout complaints in debug console, you want to give the Flow layout a reasonable .estimatedItemSize
The layout you've shown gives you an image view at 36 x 24
plus two 36 x 36
labels in a vertical stack view... so we can use an accurate size:
layout.estimatedItemSize = .init(width: 36.0, height: 96.0)
Now you can call .selectItem(at: ...)
without getting the error/warning messages.
Edit - a little clarification...
It's very common to use "auto-sizing" cells - probably more common than fixed-size cells.
When scrolling through the collection view, auto-layout processes constraints on the cell's UI elements as the cells are created/reused. Generally, as long as the constraints are setup correctly, everything works as expected.
However, when executing code that "jumps" to a cell, auto-layout uses the .estimatedItemSize
for its initial framing calculations. We can think of it as a "hint" we give to auto-layout.
UICollectionViewFlowLayout.automaticSize
is equal to:
CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
or
CGSize(width: 1.7976931348623157e+308, height: 1.7976931348623157e+308)
In this specific case, where the cell's are all equal widths (and we know that width), we can specify an exact width for .estimatedItemSize
layout.estimatedItemSize = .init(width: 36.0, height: 96.0)
to avoid the complaints.
UIKit does a LOT of work managing elements like collection views ... and it's not unusual to see these types of error/warning messages in the debug console. As long as you understand why they're being generated, and your code is producing the layout you want, you can safely ignore the warnings.