2

I am trying to implement a collection, with a few sections, with cell height depending on its content (multiline UILabel).

viewDidLoad:

    self.collectionView.delegate = self
    self.collectionView.dataSource = self

    self.collectionView.registerNib(UINib(nibName: "CustomCell", bundle: nil),
                                    forCellWithReuseIdentifier: "collectionCell")
    self.collectionView.registerNib(UINib(nibName: "CustomHeader", bundle: nil),
                                    forSupplementaryViewOfKind: UICollectionElementKindSectionHeader,
                                    withReuseIdentifier: "header")

    let flowLayout = CustomLayout()
    flowLayout.estimatedItemSize = CGSizeMake(300, 20)
    flowLayout.headerReferenceSize = CGSizeMake(300, 24)
    self.collectionView.collectionViewLayout = flowLayout

cellForItemAtIndexPath:

    let cell: CustomCell =  collectionView.dequeueReusableCellWithReuseIdentifier("collectionCell", forIndexPath: indexPath) as! CustomCell
    let section = sections[indexPath.section]
    let text = data[section]![indexPath.row]

    cell.configureWithText(text, screenWidth: self.view.frame.width, numberOfColumns: numberOfColumns)
    return cell

viewForSupplementaryElementOfKind:

    var reusableView = UICollectionReusableView()
    if kind == UICollectionElementKindSectionHeader {
        let headerView = collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader,
                                                                                  withReuseIdentifier: "header",
                                                                                  forIndexPath: indexPath) 
        (headerView as! CustomHeader).configureWithText(sections[indexPath.section], width: self.view.frame.width)
        reusableView = headerView

    }
    return reusableView

CustomCell:

@IBOutlet weak var textLabel: UILabel!

var height: CGFloat!
var width: CGFloat!

func configureWithText(text: String, screenWidth: CGFloat, numberOfColumns: CGFloat) {
    let cellWidth = (screenWidth - 40) / numberOfColumns
    self.contentView.frame.size = CGSizeMake(cellWidth, 300)
    self.textLabel.text = text
    self.textLabel.sizeToFit()
    self.width = cellWidth
    self.height = self.textLabel.frame.height + 16
    print(self.textLabel.frame.height)
}

override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
    super.preferredLayoutAttributesFittingAttributes(layoutAttributes)
    layoutAttributes.frame = CGRectMake(layoutAttributes.frame.origin.x, layoutAttributes.frame.origin.y, width, height)
    return layoutAttributes
}

CustomHeader:

@IBOutlet weak var textLabel: UILabel!
var width: CGFloat!

func configureWithText(text: String, width: CGFloat) {
    self.textLabel.text = text
    self.width = width
}

override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
    super.preferredLayoutAttributesFittingAttributes(layoutAttributes)
    layoutAttributes.frame = CGRectMake(layoutAttributes.frame.origin.x, layoutAttributes.frame.origin.y, width, 24)
    return layoutAttributes
}

In my custom layout (flow layout) shouldInvalidateLayoutForBoundsChange always return true and I am calling reloadData() on my collectionView on rotation (viewWillTransitionToSize).

Let's say I launch the app in portrait and rotate to landscape, everything works fine. But now if I rotate back to portrait I get an exception as follow:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'layout attributes for supplementary item at index path (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}) changed from <UICollectionViewLayoutAttributes: 0x7fc0a2ceb430> index path: (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 250; 768 24); zIndex = 10;  to <UICollectionViewLayoutAttributes: 0x7fc0a2cd90e0> index path: (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 407; 768 24); zIndex = 10;  without invalidating the layout'
Biniou
  • 133
  • 1
  • 9
  • The layout needs to be invalidated before it is updated. See http://stackoverflow.com/a/19208380/1172181 – Luis Mar 31 '16 at 21:38
  • 1
    it is invalidated, shouldInvalidateLayoutForBoundsChange is returning true. If it wasn't then it would crash on the first rotation, not on the second one. I tried with a more explicit invalidation calling invalidatelayout() in viewWillTransitionToSize, same result. – Biniou Mar 31 '16 at 22:22

0 Answers0