LuckyLuke
LuckyLuke

Reputation: 49087

Equal height of UICollectionViewCell's and UICollectionView

How am I supposed to set the height of the cells in UICollectionView equal to the height of whatever the collection view is? The code below doesn't work because the collection views height is not known at this point it seems since the auto layout is messing with the properties at this stage. It causes the cells height to be higher than the actual collection view.

I will add 300 in bounty to an answer that solves this!

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {

       return CGSize(width: 100, height: collectionView.frame.size.height)
    }

2015-12-16 18:43:53.643 My-app[1055:434762] the behavior of the enter code hereUICollectionViewFlowLayout is not defined because: 2015-12-16 18:43:53.643 My-app[1055:434762] the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values. 2015-12-16 18:43:53.643 My-app[1055:434762] Please check the values return by the delegate. 2015-12-16 18:43:53.644 My-app[1055:434762] The relevant UICollectionViewFlowLayout instance is , and it is attached to ; layer = ; contentOffset: {0, 0}; contentSize: {3087, 307}> collection view layout: . 2015-12-16 18:43:53.644 My-app[1055:434762] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.

Solution that works based on hannads suggestion. If there are better ways please let me know

Made a property with a property observer.

var myCollectionViewHeight: CGFloat = 0.0 {
    didSet {
        if myCollectionViewHeight != oldValue {
            myCollectionView.collectionViewLayout.invalidateLayout()
            myCollectionView.collectionViewLayout.prepareLayout()
        }
    }
}

Override this method (it is called multiple times)

override func viewDidLayoutSubviews() {
    myCollectionViewHeight = myCollectionView.bounds.size.height
}

Then I have this in my delegate:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {            
            return CGSize(width: 100, height: myCollectionViewHeight)
    }

Upvotes: 21

Views: 19173

Answers (10)

Radu Ursache
Radu Ursache

Reputation: 1481

Keep "Estimated Size" to "Automatic" and add this to your UIViewController to make sure the UICollectionView layout get updated after the content view got rendered:

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()
  self.recentAthletesCollectionView.collectionViewLayout.invalidateLayout()
}

Upvotes: 0

Vlad  Antonov
Vlad Antonov

Reputation: 348

I solved this problem with this solution:

  1. Cell size is equal collectionView size

enter image description here 2. Change in storyboard "Inset from:" from Safe Area to Content Inset

enter image description here

  1. Change "Content Insets" to Never enter image description here

Upvotes: 1

This problem can be easily solved by setting the collectionViewLayout itemSize property inside your viewdidload:

collectionViewLayout.itemSize = .init(width: 80, height: 80)

Upvotes: 0

Jonny
Jonny

Reputation: 2094

In my case, I have to set collectionViewLayout.estimatedItemSize to an explicit value (which has its height <= collection view's height) instead of UICollectionViewFlowLayout.automaticSize.

The UICollectionViewFlowLayout.automaticSize results a default 50pt by 50pt cell size, which is higher than my collection view. In this case, UIKit refuses to do any layout work, despite that the actual cell size after self-sizing calculation will be smaller.

// horizontally scrollable collection view

private var viewHeight: CGFloat?

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    
    if viewHeight != view.bounds.height {
        viewHeight = view.bounds.height
        layout.estimatedItemSize = CGSize(width: 50, height: viewHeight!)
        layout.invalidateLayout()
    }
}

Upvotes: 2

Jacobo Koenig
Jacobo Koenig

Reputation: 12514

I had been struggling with this issue for over a week and finally found an answer that worked for my specific case. My issue was a result of 1) loading remote images in the UICollectionViewCell's image, and 2) having an estimated cell size that was larger than what I set manually on the sizeForItemAt of the collection view layout.

enter image description here

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: collectionView.bounds.width, height: collectionView.bounds.height)
}

I was able to solve by reducing the cell size on the storyboard to an amount lower than my cell's size, and I also set "Estimate Size" to "None" (this last part is probably enough)

Upvotes: 6

wlixcc
wlixcc

Reputation: 1492

In my case , I add collectionview to tableviewcell, and collectionview item height is equal to tableviewcell height. when change tableviewcellheiht, i get this warning. to solve this , try this code

  if (@available(iOS 11.0, *)) {
        _collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    } else {
    }

Upvotes: 0

W.K.S
W.K.S

Reputation: 10095

I had this issue because I had pinned my UICollectionView to the bottom of a UITableViewCell without giving the collectionView a height.

After making a few network requests and then calling [self.tableView reloadData] in the UIViewController, I got the same error message in the log.

I managed to resolve it by breaking the bottom constraint and giving the UICollectionView a fixed height.

Upvotes: 1

hannad
hannad

Reputation: 822

Here is how I would do it. First add a global variable in the view controller which will be used to store the height of the collection view. In the viewDidLayoutSubviews method, set this variable to the height of the collectionView, and if it changed, call a method to invalidate the UICollectionView layout

collectionView.collectionViewLayout.invalidateLayout()

Which will call the method to set sizes of the collectionView cells, and in that method, set the height of the cell to the global variable holding the height of the collectionView.

Note: currently on my mobile and did not test this code. I might have missed something.

Upvotes: 12

bolnad
bolnad

Reputation: 4583

I ran into this issue as well and i was able to solve it by subtracting the relevant sectionInsets. Once you subtract the insets that aren't considered in the .size.height property it accepts the height.

Inside sizeForItemAtIndexPath you need to subtract the section inset dimensions...

  let sectionInset = self.collectionView?.collectionViewLayout.sectionInset
  let heightToSubtract = sectionInset!.top + sectionInset!.bottom

  return CGSize(width: 100.0, height: (self.collectionView?.bounds.height)! - heightToSubtract)

NOTE: I am force unwrapping the optionals here but to be safe you might want to check them

Upvotes: 0

vien vu
vien vu

Reputation: 4337

You try set this code to your viewDidLoad:

self.automaticallyAdjustsScrollViewInsets = false

Hope this help!

Upvotes: 11

Related Questions