I am working with "modern" UICollectionView with iOS14 to implement a List configuration. My implementation is successful, ie cells are rendered with dynamic height sizing, but with conflicting constraints.
2020-12-24 19:23:50.889822+0800 circulars[18490:2636547] [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:0x60000102b250 V:|-(16)-[UIStackView:0x7f9b6ad4d120] (active, names: '|':circulars.CircularCollectionCell:0x7f9b6ad4c5f0 )>",
"<NSLayoutConstraint:0x60000102b4d0 UIStackView:0x7f9b6ad4d120.bottom == circulars.CircularCollectionCell:0x7f9b6ad4c5f0.bottom - 16 (active)>",
"<NSLayoutConstraint:0x60000102b520 'UISV-canvas-connection' UIStackView:0x7f9b6ad4d120.top == UILabel:0x7f9b6ad49320.top (active)>",
"<NSLayoutConstraint:0x60000102b570 'UISV-canvas-connection' V:[UILabel:0x7f9b6ad4ceb0]-(0)-| (active, names: '|':UIStackView:0x7f9b6ad4d120 )>",
"<NSLayoutConstraint:0x60000102b5c0 'UISV-spacing' V:[UILabel:0x7f9b6ad49320]-(8)-[UILabel:0x7f9b6ad4c930] (active)>",
"<NSLayoutConstraint:0x60000102b610 'UISV-spacing' V:[UILabel:0x7f9b6ad4c930]-(8)-[UILabel:0x7f9b6ad4ceb0] (active)>",
"<NSLayoutConstraint:0x60000101dd60 'UIView-Encapsulated-Layout-Height' circulars.CircularCollectionCell:0x7f9b6ad4c5f0.height == 44 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60000102b610 'UISV-spacing' V:[UILabel:0x7f9b6ad4c930]-(8)-[UILabel:0x7f9b6ad4ceb0] (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.
Code:
class TestController: UIViewController {
lazy var collectionView: UICollectionView = {
let config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
let layout = UICollectionViewCompositionalLayout.list(using: config)
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.backgroundColor = .systemBackground
cv.delegate = self
return cv
}()
var dataSource: UICollectionViewDiffableDataSource<Section, Circular>?
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupData()
}
func setupViews() {
view.backgroundColor = .systemBackground
navigationItem.title = "CV"
view.addSubview(collectionView)
collectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
setupCell()
}
func setupCell() {
//No constraint conflict for this cell section
let registration = UICollectionView.CellRegistration<UICollectionViewListCell, Circular> { (cell, indexPath, circular) in
var content = cell.defaultContentConfiguration()
content.text = circular.title
cell.contentConfiguration = content
}
//Constraint conflict for this cell/section.
let cirRegistration = UICollectionView.CellRegistration<CircularCollectionCell, Circular> { (cell, indexPath, circular) in
cell.circular = circular
}
dataSource = UICollectionViewDiffableDataSource<Section, Circular>(collectionView: collectionView) { (collectionView, indexPath, circular) -> UICollectionViewCell? in
if indexPath.section == 0 {
return collectionView.dequeueConfiguredReusableCell(using: registration, for: indexPath, item: circular)
} else {
return collectionView.dequeueConfiguredReusableCell(using: cirRegistration, for: indexPath, item: circular)
}
}
}
func setupData() {
let circulars = [
//Setup circulars
]
populate(circulars: circulars)
}
func populate(circulars: [Circular]) {
var snapshot = NSDiffableDataSourceSnapshot<Section, Circular>()
snapshot.appendSections([.main, .second])
snapshot.appendItems(circulars, toSection: .main)
snapshot.appendItems(Array(circulars.prefix(2)), toSection: .second)
dataSource?.apply(snapshot)
}
}
extension TestController {
enum Section {
case main, second
}
}
//At custom CircularCollectionCell
class CircularCollectionCell: UICollectionViewListCell {
var companyLabel: UILabel = {
let l = UILabel()
l.translatesAutoresizingMaskIntoConstraints = false
l.numberOfLines = 0
l.font = UIFont.systemFont(ofSize: 15, weight: .heavy)
return l
}()
var titleLabel: UILabel = {
let l = UILabel()
l.translatesAutoresizingMaskIntoConstraints = false
l.numberOfLines = 0
l.font = UIFont.systemFont(ofSize: 17, weight: .medium)
return l
}()
var dateLabel: UILabel = {
let l = UILabel()
l.translatesAutoresizingMaskIntoConstraints = false
l.numberOfLines = 0
l.font = UIFont.systemFont(ofSize: 14, weight: .light)
l.textColor = .systemGray
return l
}()
var circular: Circular? {
didSet {
if let circular = circular {
//Setup UI
dateLabel.text = circular.date
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
let stackView = UIStackView(arrangedSubviews: [
companyLabel,
titleLabel,
dateLabel
])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.spacing = 8
addSubview(stackView)
stackView.topAnchor.constraint(equalTo: topAnchor, constant: 16).isActive = true
stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16).isActive = true
stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16).isActive = true
stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16).isActive = true
}
}