How to adjust height of UICollectionView to be the height of the content size of the UICollectionView?
Solution 1
I would suggest the following:
- Add a height constraint to your collection view.
- Set its priority to 999.
- Set its constant to any value that makes it reasonably visible on the storyboard.
- Change the bottom equal constraint of the collection view to greater or equal.
- Connect the height constraint to an outlet.
- 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
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...
Dynamic cell width of UICollectionView depending on label width
Solution 3
1) Set Fix Height of your CollectionView.
2) Create Outlet of this CollectionView Height Constant. Like :
IBOutlet NSLayoutConstraint *constHeight;
3) Add below method in your .m file:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
CGFloat height = collectionMenu.collectionViewLayout.collectionViewContentSize.height;
constHeight.constant = height;
}
Solution 4
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
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
Related videos on Youtube
charbinary
Updated on July 08, 2022Comments
-
charbinary almost 2 years
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).-
dahiya_boy about 7 yearsYou've to take height constraint outlet of ColletionView and adjust height like this
heightConstraint.constant = collectionView.contentSize.height
-
Georgi Boyadzhiev about 7 yearsAre You using custom layout or the provided by Apple - UICollectionViewFlowLayout?
-
charbinary about 7 yearsI am using the default one UICollectionViewFlowLayout
-
Georgi Boyadzhiev about 7 yearsThan You should use collectionViewLayout.collectionViewContentSize, see @hasan83's answer. It is a possibility for You.
-
-
Georgi Boyadzhiev about 7 yearsThis method returns the size for a specific cell at index path, if You set the height to noOfCell*heightOfCell this will set the height of all the cells.
-
charbinary about 7 years@Georgi Boyadzhiev, that's true.
-
nr5 over 6 yearsMine expands to some extent but not perfect. It misses about 3 4 lines of the label. Any idea? Here is how it looks: ibb.co/jeO9Rv ibb.co/iV8b6v
-
hasan over 6 years@Nil Do you have a header or footer on your collection view? the height of the footer and the header must be added.
-
nr5 over 6 yearsNevermind, it worked. The height constraint was set to proportional height that's why setting just only the constant was making some difference not much.
-
nr5 over 6 yearsWhile we are talking, do you have any idea how to pin a cell to the top? In my horizontal collectionview some cells are not pinned to the top. Just randomly floating in middle. Here: ibb.co/fHbSwv
-
Mitesh Dobareeya over 6 years@hasan83 I am getting some extra height of collection view by this way, any suggestion ?
-
hasan over 6 years@MiteshDobareeya hmm, maybe its something with the collection view headers and footers? did you add any view in the collection view on the storyboard?
-
hasan over 6 yearsor maybe its with point 4. Change the bottom equal constraint of the collection view to greater or equal. Greater or equal which means the height of the collectionview should always be less or equal to the height of its parent view
-
Mitesh Dobareeya over 6 years@hasan83 No i have not added any view in collection view or no any header and footers are there. In my design i have just scroll view and collection view is inside content view of scrollview and 2 other views. All have fix height and collection view has fix height outlet. So when i reloading collection view, i am just getting hight based on it's content and assigning to its height constant. But unfortunately getting some extra height.
-
hasan over 6 yearswhat I proposed in my answer doesn't work as it is on a scrollview. thats the problem. you got the idea on how you can do that on a view. so, think the same to figure out a solution for your situation
-
Carl about 5 yearsLike others in the comments I was getting some extra height when using this method. To fix this I simply did a
.reloadData()
and then changed the constant of the height constraint within a collection viewperformBatchUpdates
. -
Pankaj Kulkarni almost 5 yearsThis worked using
Swift 5
andXcode 10.2.1
. Thanks. -
cabyambo almost 5 yearsThis works to make the
UICollectionView
initially the size of its content, but if the content changes ie: insert or delete rows, it doesn't resize. And if I call reload data the collection view stutters as it resizes. Any way to stop this stuttering? -
d4Rk almost 5 yearsDid you try calling
self.invalidateIntrinsicContentSize()
without callingreloadData()
? Maybe this could work. -
n13 almost 5 yearsBy far the most beautiful solution - at least for my use case. Thank you. It solves the problem of timing, layout needs all other constraints and views to be sized to determine content size - this one just works.
-
n13 almost 5 yearsActually - I had to constrain the intrinsic size to be at least 1 - never 0 - for this to correctly work. Otherwise there were small errors in the calculation, cutting off the bottom of the view by a few pixels.
-
n13 almost 5 yearsTried, but that didn't work for me. The other subclassing solution by d4Rk worked with minor modifications
-
ArielSD over 4 yearsAre you able to use this programmatically as well, or just with storyboards?
-
hasan over 4 yearsYou can add the constraints programmatically. if that what you mean @ArielSD we already have some Code involved even with the storyboard version!
-
ArielSD over 4 years@hasan The collectionView I'm using is nested in a stackView in a tableViewCell - where should I call
setNeedsLayout
? -
AnthonyMDev over 4 yearsWhy does this mean you lose cell re-use? Not clear on what is causing that using this method.
-
d4Rk over 4 years@AnthonyMDev Because using this approach, you're resizing the collection view to fit all items at the same time, so all items are loading any time.
-
AnthonyMDev over 4 yearsAhhhh that makes sense. I was thinking it was breaking the actual dequeue logic, but it's just a fact of the collection view displaying all the cells at all times. Thanks for the response!
-
Karthi Rasu about 4 yearswhat does it myCollectionView in your code.Where you declared
-
Naresh about 4 years@ Karthi Rasu, myCollectionView is your collection view height constraint. If you are using storyboard fix height of collectionView.
-
Teddy K over 3 yearsThis was the only thing that worked for me on iOS 14. Thanks!
-
Vinayak Bhor over 3 yearsPerfect solution !!
-
Pranav Pravakar over 3 years@cabyambo Were you able to find the solution for it?
-
QuangDT over 3 yearsIf you find the contentsize returned is higher than actual, set the initial height constant to something large like 1000.
-
Andy Weinstein almost 3 yearsI use RxSwift to detect when the height of the collection view changes (like when I configure it by calling reloadData after updating the relevant data). In the closure that is invoked by the observer, I do the update of the height constraint and call layoutIfNeeded - also on the superviews. The code for the observer is: collectionView.rx.observe(CGSize.self , "contentSize").subscribe(onNext: { (size) in <your code here>}
-
Naresh over 2 yearsDynamic width for cell... stackoverflow.com/questions/23134986/…
-
iosMentalist over 2 yearscan u share the full code ?
-
Wimukthi Rajapaksha over 2 yearsif you're using UIScrollView as parent of the collection view, need to do slight change. Try, stackoverflow.com/a/70285535/10505343
-
cristian_064 almost 2 yearsthat is very weird I don't know why need to setup first value 1 to start calculate the final value.