I'd like to display a bunch of UICollection view cells with images and labels and adjust their widths according to width of the labels.
I don't want to override sizeThatFits()
neither to override preferredLayoutAttributesFitting
As far as I understand all I need to do is to just make sure that AutoLayout in my cell is OK and then use property estimatedItemSize
to make cells self sizing.
Unfortunately it's kinda true in my case. UI looks fine but when I rotate the device I've got plenty of broken constraints. However when I just use the property itemSize
and make all my cells with fixed size (like 100x150) then everything is fine during rotations, no AutoLayout issues.
This is how I constrained my UICollectionViewCell. I don't really know why I had to use dummy UIView
as a "main" view for UIStackView
but Xcode cried a lot with AutoLayout issues without it. For testing purposes I put this view outside cell but in separate UIViewController
to test if AutoLayout is OK - seems like it is.
And this code for my ViewController
:
import UIKit
class ViewController: UIViewController {
@IBOutlet var myCollectionView: UICollectionView!
let items: [Item] = [
Item(title: "Cell 1"),
Item(title: "Cell 2"),
Item(title: "Cell 3"),
Item(title: "Cell 4"),
Item(title: "Cell 5"),
Item(title: "Cell 6")
]
override func viewDidLoad() {
super.viewDidLoad()
guard let layout = myCollectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
fatalError("Impossibruuu - layout is not FlowLayout")
}
myCollectionView.register(UINib(nibName: "MyCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "MyCollectionViewCell")
layout.minimumInteritemSpacing = 10
layout.minimumLineSpacing = 50
// line below makes AutoLayout angry during rotations
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
// this is completely fine
//layout.itemSize = CGSize(width: 100, height: 150)
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCollectionViewCell", for: indexPath) as? MyCollectionViewCell else { return UICollectionViewCell() }
cell.text = items[indexPath.row].title
return cell
}
}
Unfortunately when using layout.estimatedItemSize
I've got completely bizarre log:
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.
2019-06-22 18:53:18.841709+0200 CollectionViewSelfSizing[55280:2953587] [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:0x600003d6e170 UIImageView:0x7fe820c1ac90.height == 100 (active)>",
"<NSLayoutConstraint:0x600003d6ebc0 V:[UIStackView:0x7fe820c1a020]-(0)-| (active, names: '|':UIView:0x7fe820c19e40 )>",
"<NSLayoutConstraint:0x600003d6ec60 V:|-(0)-[UIStackView:0x7fe820c1a020] (active, names: '|':UIView:0x7fe820c19e40 )>",
"<NSLayoutConstraint:0x600003d6ed00 V:[UIView:0x7fe820c19e40]-(0)-| (active, names: '|':UIView:0x7fe820c19c60 )>",
"<NSLayoutConstraint:0x600003d6eda0 V:|-(0)-[UIView:0x7fe820c19e40] (active, names: '|':UIView:0x7fe820c19c60 )>",
"<NSLayoutConstraint:0x600003d6cd70 'UISV-canvas-connection' UIStackView:0x7fe820c1a020.top == UIImageView:0x7fe820c1ac90.top (active)>",
"<NSLayoutConstraint:0x600003d6cf50 'UISV-canvas-connection' V:[UILabel:0x7fe820c1d0c0'Cell 1']-(0)-| (active, names: '|':UIStackView:0x7fe820c1a020 )>",
"<NSLayoutConstraint:0x600003d6ce60 'UISV-spacing' V:[UIImageView:0x7fe820c1ac90]-(19)-[UILabel:0x7fe820c1d0c0'Cell 1'] (active)>",
"<NSLayoutConstraint:0x600003d644b0 'UIView-Encapsulated-Layout-Height' UIView:0x7fe820c19c60.height == 50 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600003d6e170 UIImageView:0x7fe820c1ac90.height == 100 (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.