2

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.

enter image description here

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.
bdolewski
  • 163
  • 1
  • 11
  • You've only got two views, the image view and the label, so what's the stack view for? – matt Jun 22 '19 at 18:28
  • Well, to reduce amount of constraints, I think. Maybe it is a overkill, I'll try without stack view (so `UIImageView` constraint to top with 0, height == width == 100 and centred horizontal. `UILabel` constrained with 19 points below image, leading and trailing with 0 and no bottom constraint. Let me check that. – bdolewski Jun 22 '19 at 18:44
  • Stack views do not reduce the amount constraints. They increase them! – matt Jun 22 '19 at 18:46
  • Well, I tried what I told. Not only I've got warnings during rotations but my layout looks very bad (at least it was perfectly fine previously). – bdolewski Jun 22 '19 at 18:49
  • I'm ready to think that `estimatedItemSize` + auto-layout simply does not work anymore :) I dunno, I've created crazy simple pet-project to test this (needed for my work) but I think I will stick with `collectionView(_:layout:sizeForItemAt:)` – bdolewski Jun 22 '19 at 18:52
  • Well, I think it has _never_ worked. I (and many others) have provided workarounds here: https://stackoverflow.com/questions/51375566/in-ios-12-when-does-the-uicollectionview-layout-cells-use-autolayout-in-nib (In fact I was tempted to mark your question a duplicate of that one.) However, those answers generally still expect you to know how to use autolayout. – matt Jun 22 '19 at 20:15
  • Thanks! I'm not sure if link provided is a duplicate because I've got layout right but issues during rotation. I studied all solutions, even tried `layoutAttributes.size = contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)` in method `preferredLayoutAttributesFitting(_:)` but no luck. Also I removed all my XIB file for custom cell, created everything in code (subviews, constraints, no stack view). Result? Still got issues during rotation. I submitted bug to Apple with ful Xcode project included. The only way to fix it is to use `itemSize` or delegate method. End of story. – bdolewski Jun 23 '19 at 09:27

0 Answers0