How to adjust height of UICollectionView to be the height of the content size of the UICollectionView?
IosSwiftUicollectionviewNslayoutconstraintIos AutolayoutIos Problem Overview
I would like the UICollectionView (The red one) to shrink to the height of the content size in this case UICollectionViewCells(the yellow ones) because there is a lot of empty space. What I tried is to use:
override func layoutSubviews() {
super.layoutSubviews()
if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
return self.collection.contentSize
}
but return self.collection.contentSize
always return (width, 0)
and for this reason it shrinks too much to value of height 30 (The value which I set in the XIB file for the height, although I have constaint >= 30).
Ios Solutions
Solution 1 - Ios
I would suggest the following:
> 1. Add a height constraint to your collection view. > 2. Set its priority to 999. > 3. Set its constant to any value that makes it reasonably visible on the storyboard. > 4. Change the bottom equal constraint of the collection view to greater or equal. > 5. Connect the height constraint to an outlet. > 6. Every time you reload the data on the collection view do the following:
You may also want to consider the Inset of the collection view by adding it to the content size.
Code Sample:
CGFloat height = myCollectionView.collectionViewLayout.collectionViewContentSize.height
heightConstraint.constant = height
self.view.setNeedsLayout() Or self.view.layoutIfNeeded()
Explanation: Extra, You don't have to read if you understand it. obviously!!
The UI will try to reflect all the constraints no matter what are their priorities. Since there is a height constraint with lower priority of (999), and a bottom constraint of type greater or equal. whenever, the height constraint constant is set to a value less than the parent view height the collection view will be equal to the given height, achieving both constraints.
But, when the height constraint constant set to a value more than the parent view height both constraints can't be achieved. Therefore, only the constraint with the higher priority will be achieved which is the greater or equal bottom constraint.
The following is just a guess from an experience. So, it achieves one constrant. But, it also tries to make the error in the resulted UI for the other un-achieved lower priority constraint as lowest as possible. Therefore, the collection view height will be equal to the parent view size.
Solution 2 - Ios
In Swift 5 and Xcode 10.2.1
My CollectionView name is myCollectionView
-
Fix height for your CollectionView
-
Create Outlet for your CollectionViewHeight
IBOutlet weak var myCollectionViewHeight: NSLayoutConstraint!
-
Use below code
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() let height = myCollectionView.collectionViewLayout.collectionViewContentSize.height myCollectionViewHeight.constant = height self.view.layoutIfNeeded() }
Dynamic width for cell based on text content...
Solution 3 - Ios
-
Set Fix Height of your CollectionView.
-
Create Outlet of this CollectionView Height Constant. Like :
> IBOutlet NSLayoutConstraint *constHeight;
-
Add below method in your .m file:
-
(void)viewDidLayoutSubviews { [super viewDidLayoutSubviews];
CGFloat height = collectionMenu.collectionViewLayout.collectionViewContentSize.height; constHeight.constant = height; }
-
Solution 4 - Ios
I ended up, by subclassing the UICollectionView
and overriding some methods as follows.
- Returning
self.collectionViewLayout.collectionViewContentSize
forintrinsicContentSize
makes sure, to always have the correct size - Then just call it whenever it might change (like on
reloadData
)
Code:
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
return self.collectionViewLayout.collectionViewContentSize
}
But be aware, that you lose "cell re-using", if you display large sets of data, eventhough they don't fit on the screen.
Solution 5 - Ios
This seemed like the simplest solution for me.
class SelfSizingCollectionView: UICollectionView {
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
isScrollEnabled = false
}
override var contentSize: CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
return contentSize
}
}
You may not need to override reloadData
Solution 6 - Ios
You have to set height constraint as equal to content size
HeightConstraint.constant = collection.contentSize.height
Solution 7 - Ios
Took the solution by d4Rk which is great, except in my case it would keep cutting off the bottom of my collection view (too short). I figured out this was because intrinsic content size was sometimes 0 and this would throw off the calculations. IDK. All I know is this fixed it.
import UIKit
class SelfSizedCollectionView: UICollectionView {
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
let s = self.collectionViewLayout.collectionViewContentSize
return CGSize(width: max(s.width, 1), height: max(s.height,1))
}
}
Solution 8 - Ios
- Subclass UICollectionView as follows
- Delete height constraint if any
- Turn on Intrinsic Size
class ContentSizedCollectionView: UICollectionView {
override var contentSize:CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
layoutIfNeeded()
return CGSize(width: UIView.noIntrinsicMetric, height: collectionViewLayout.collectionViewContentSize.height)
}
}
Solution 9 - Ios
Do following.
-
first set height constrain for
UICollectionView
-
here
calendarBaseViewHeight
isUICollectionView
height Variable -
call the function after reload the collection view
func resizeCollectionViewSize(){ calendarBaseViewHeight.constant = collectionView.contentSize.height }
Solution 10 - Ios
I have a multi-line, multi-selection UICollectionView
subclass where the cells are of fixed height and left-aligned flowing from left to right. It's embedded in a vertical stack view that's inside a vertical scroll view. See the UI component below the label "Property Types".
In order for the collection view to fit the height of its contentSize
, here's what I had to do (note that this is all within the UICollectionView
subclass):
-
Give the collection view a non-zero minimum height constraint of priority
999
. Auto-sizing the collection view to its content height simply won't work with zero height.let minimumHeight = heightAnchor.constraint(greaterThanOrEqualToConstant: 1) minimumHeight.priority = UILayoutPriority(999) minimumHeight.isActive = true
-
Set the collection view's content hugging priority to
.required
for the vertical axis.setContentHuggingPriority(.required, for: .vertical)
-
Calling
reloadData()
is followed by the following calls:invalidateIntrinsicContentSize() setNeedsLayout() layoutIfNeeded()
For example, I have a
setItems()
function in my subclass:func setItems(_ items: [Item]) { self.items = items selectedIndices = [] reloadData() invalidateIntrinsicContentSize() setNeedsLayout() layoutIfNeeded() }
-
Override
contentSize
andintrinsicContentSize
as follows:override var intrinsicContentSize: CGSize { return contentSize } override var contentSize: CGSize { didSet { invalidateIntrinsicContentSize() setNeedsLayout() layoutIfNeeded() } }
Solution 11 - Ios
first of all calculate number of cells than multiply it with height of cell and then return height in this method
collectionView.frame = CGRectMake (x,y,w,collectionView.collectionViewLayout.collectionViewContentSize.height); //objective c
//[collectionView reloadData];
collectionView.frame = CGRect(x: 0, y: 0, width: width, height: collectionView.collectionViewLayout.collectionViewContentSize.height) // swift
Solution 12 - Ios
If you set the height constraint of the collection view. Just observe the contentSize
change in the viewDidLoad and update the constraint.
self.contentSizeObservation = collectionView.observe(\.contentSize, options: [.initial, .new]) { [weak self] collectionView, change in
guard let `self` = self else { return }
guard self.collectionView.contentSize != .zero else { return }
self.collectionViewHeightLayoutConstraint.constant = self.collectionView.contentSize.height
}
Solution 13 - Ios
On your UICollectionView
set your constraints such as Trailing
, Leading
, and Bottom
:
If you look at my height constraint in more detail, as it is purely for storyboard look so I don't get errors, I have it to Remove at build time
. The real height constraint is set in my code down below.
My code for DrawerCollectionView
, which is set as the collection view Custom Class
:
import UIKit
class DrawerCollectionView: UICollectionView {
override func didMoveToSuperview() {
super.didMoveToSuperview()
heightAnchor.constraint(equalToConstant: contentSize.height).isActive = true
}
}
Solution 14 - Ios
work for me
let heightRes = resCollectionView.collectionViewLayout.collectionViewContentSize.height
foodHeightConstrant.constant = height.advanced(by: 1 )
foodCollectionView.setNeedsLayout()
foodCollectionView.layoutIfNeeded()
Solution 15 - Ios
Adjusting height of UICollectionView to the height of it's content size
SWIFT 5
final class MyViewController: UIViewController {
// it's important to declare layout as separate constant due to late update in viewDidLayoutSubviews()
private let layout = UICollectionViewFlowLayout()
private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
override func viewDidLoad() {
super.viewDidLoad()
setupCollectionView()
setupCollectionViewConstraints()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
updateFlowLayout()
}
private func setupCollectionView() {
view.addSubview(collectionView)
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "UICollectionViewCell")
collectionView.dataSource = self
}
private func setupCollectionViewConstraints() {
// your collectionView constraints setup
}
private func updateFlowLayout() {
let height = collectionView.collectionViewLayout.collectionViewContentSize.height
layout.itemSize = CGSize(width: view.frame.width, height: height)
layout.scrollDirection = .horizontal
layout.minimumInteritemSpacing = .zero
layout.minimumLineSpacing = .zero
layout.sectionInset = UIEdgeInsets.zero
}
}
extension MyViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {...}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {...}
}
Solution 16 - Ios
Get the height of the cell. Something like this
let cellHeight = cell.frame.height
Get the origin of the collection view
let cvOrigin = collectionView.frame.origin
Get the width of the collection view
let cvWidth = collectionView.bounds.width
Set the frame of the content view
collection.frame = CGRect(x: cvOrigin.x, y: cvOrigin.y, width: cvWidth, height: cellHeight )