I have a UICollectionView with a reusable header (that's why I'm using collection and not tableview). The cell needs to have a variable height, depending on a UILabel. I know in UITableView you can use UITableViewAutomaticDimension to auto resize the cell, but I can't find an analogous function for UICollectionView. Any idea? I suppose I should start with sizeForItemAtIndexPath ?
override func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let height = // needs to be variable
let width = UIScreen.mainScreen().bounds.width
return CGSize(width: width, height: height)
}
Setup the cell with auto layout constraints then set the estimatedItemSize property of the your UICollectionView's UICollectionViewFlowLayout. For example:
self.collectionView.collectionViewLayout.estimatedItemSize = CGSize(100,100)
Related
I have UICollectionView inside UITableViewCell. If I make the size of the CollectionviewCell as custom and provide fixed width then it works fine.
But if I make it automatic and assign size in the delegate then it doesn't work. It takes the default width i.e 50.
Can you please help me out?
Thanks
Code for changing size:
extension BeInspiredTVC : UICollectionViewDelegate , UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width - 50, height: 224)
}
}
I want a full width Collectionviewcell but I am getting like the image below.
take collection view width constraint
next assign self.contentView.frame.width to that constraint in layoutsubsview in tableview cell.
I have a UICollectionView and I have implemented the delegate to calculate the width of my cells for 0 spacing between cells.
It works great on its own, but when I have it inside a container view less than the size of the device, iOS incorrectly works out the spacing between the cells adding a horizontal space I don't want.
I have verified the width I am using to calculate the cell size is correct, so I'm not sure what is causing the problem.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let widthPerItem = view.frame.width / itemsPerRow
return CGSize(width: widthPerItem, height: 60)
}
You are calculating your cell size based upon the size of the view. Since the collectionView doesn't take up the full width of the screen, you are getting the incorrect value. Instead, you should base your calculation on the width of the collectionView itself.
Use the bounds of the collectionView instead of the frame of the view:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let widthPerItem = collectionView.bounds.width / itemsPerRow
return CGSize(width: widthPerItem, height: 60)
}
Ok, the problem was actually related to my uicollectionviewcontroller not correctly sizing to it's parent container view. Changing to a uiviewcontroller with an embedded uicollectionview plus constraints fixed the problem.
I have a UICollectionView with horizontal scroll and variable sized items.
Is it possible to have constant spacing between items in 'horizontal' rows?
Thanks.
You need to provide cell's variable width. Implement this method from UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let size = (items[indexPath.item] as NSString).size(attributes: nil)
return CGSize(width: size.width + yourTextPadding, height: yourCellHeight)
}
You can also provide label's font attributes for exact computation or just use some constant (like 2 times width of text).
I have a storyboard, consisting of a single UICollectionView with multiple cells, each of varying height. The first cell takes the height from the UICollectionViewDelegateFlowLayout:
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
.. but I'd like the second cell to be shorter.
I've placed two UIStackViews inside a "master" UIStackView inside the cell, and each of the inner UIStackViews has one or more labels, like this:
cell
--> stackView (master)
--> stackView (1)
--> label
--> stackView (2)
--> label
--> label (etc)
.. in the hope that the UIStackView would make the cell height dynamic, but it doesn't. It takes the height from the UICollectionViewDelegateFlowLayout as before.
How should I be doing this?
You need to compute the size of the content for the CollectionViewCell and return it to the sizeForItemAt function.
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
// Create an instance of the `FooCollectionViewCell`, either from nib file or from code.
// Here we assume `FooCollectionViewCell` is created from a FooCollectionViewCell.xib
let cell: FooCollectionViewCell = UINib(nibName: "FooCollectionViewCell", bundle: nil)
.instantiate(withOwner: nil, options: nil)
.first as! FooCollectionViewCell
// Configure the data for your `FooCollectionViewCell`
cell.stackView.addArrangedSubview(/*view1*/)
cell.stackView.addArrangedSubview(/*view2*/)
// Layout the collection view cell
cell.setNeedsLayout()
cell.layoutSubviews()
// Calculate the height of the collection view based on the content
let size = cell.contentView.systemLayoutSizeFitting(
CGSize(width: collectionView.bounds.width, height: 0),
withHorizontalFittingPriority: UILayoutPriorityRequired,
verticalFittingPriority: UILayoutPriorityFittingSizeLevel)
return size
}
With this, you will have a dynamic cell heights UICollectionView.
Further notes:
for configuration of the collection view cell, you can create a helper function func configure(someData: SomeData) on FooCollectionViewCell so that the code could be shared between the cellForItemAt function and sizeForItemAt function.
// Configure the data for your `FooCollectionViewCell`
cell.stackView.addArrangedSubview(/*view1*/)
cell.stackView.addArrangedSubview(/*view2*/)
For these two lines of code, it seems only needed if the UICollectionViewCell contains vertical UIStackView as subViews (likely a bug from Apple).
// Layout the collection view cell
cell.setNeedsLayout()
cell.layoutSubviews()
If you'd like to change the height of your cells you're going to have to change the height you return in the sizeForItemAtIndexPath. The stack views aren't going to have any effect here. Here's an example of what you can do:
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if indexPath.row == 1 {
return CGSizeMake(width, height/2)
}
return CGSizeMake(width, height)
}
This will change the size of your cells at row 1. You can also use indexPath.section to choose sections. Hope this helps.
I have a CollectionViewCell placed in a CollectionView. The size of the CollectionView is set via Auto Layout and can change at times.
I want to (programmatically) set constraints on my CollectionView, so that the size of the CollectionViewCell is responsive to the size of the CollectionView (Say the width of the Cell is equal to the width of the CollectionView-100 and the height should be equal).
How can I declare constraints between the Cell and the CollectionView?
When, where and how do set those constraints? Do I have to call setNeedsLayout on the Cells when the size of the CollectionView changes?
I searched for this quite long and all I found was always about changing the size of the Cell according to its content – that’s not what I am trying to do. The only hint that I got is that I might have to subclass CollectionViewLayout – but I’m not sure about that.
Any help is appreciated!
You cannot achieve this using constraints, but you can easily do this using UICollectionViewDelegate's sizeForItemAtIndexPath method. Example:
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSize(width: collectionView.frame.size.width / 2, height: collectionView.frame.size.height/ 2)
}
And after changing the collectionView size all you need to do is call its reloadData method.
I can suggest for you 2 ways how to do this:
In your UIViewController try to set the collectionViewLayout property in viewDidLayoutSubviews method. I'm not sure if it works when screen orientation will change.
- (void)viewDidLayoutSubviews
{
UICollectionViewFlowLayout *flow = YOUR_COLLECTION_VIEW.collectionViewLayout;
float width = CGRectGetWidth(YOUR_COLLECTION_VIEW.frame)-100.f;
float height = CGRectGetHeight(YOUR_COLLECTION_VIEW.frame);
YOUR_COLLECTION_VIEW.collectionViewLayout = flow;
}
If that doesn't work, then I'm 100% sure that it will work if you subclass the collectionView and insert the same code but in (void)layoutSubviews
Set the cell height and get the collection view height in UICollectionViewDelegateFlowLayout sizeForItemAt method. You can set the cell size based on a whole number or multiply as a percentage if that's more appropriate.
func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout, sizeForItemAt
indexPath: IndexPath) -> CGSize {
let heigh = collectionView.frame.height
let widthOfCell = collectionView.frame.width - 100 // OP request
return CGSize(width: width, height: height)
}