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)
}
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.
On an iPhone 6 Plus, the collection view cells are fine, but when testing on another device size like the iPhone 5, i'm bombarded with "
017-06-15 01:59:25.744 HyperTest[3865:8825385] The behavior of the UICollectionViewFlowLayout is not defined because:
2017-06-15 01:59:25.744 HyperTest[3865:8825385] the item width must be less than the width of the UICollectionView minus the section insets left and right values, minus the content insets left and right values.
2017-06-15 01:59:25.745 HyperTest[3865:8825385] The relevant UICollectionViewFlowLayout instance is , and it is attached to ; layer = ; contentOffset: {0, 0}; contentSize: {414, 219}> collection view layout: .
2017-06-15 01:59:25.745 HyperTest[3865:8825385] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
especially when I do this:
let layout = billFeedCollectionView.collectionViewLayout as? UICollectionViewFlowLayout // casting is required because UICollectionViewLayout doesn't offer header pin. It's feature of UICollectionViewFlowLayout
layout?.sectionHeadersPinToVisibleBounds = true
layout?.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
anyway, I can resolve this problem? I need to ensure that the cell scales and fits all devices.
By confirming UICollectionViewDelegate from your class. The delegate method sizeForItemAtIndexPath will call from UICollectionViewDelegateFlowLayout and this will call in your class now you can return the size of UICollectionViewCell.
This works for me
method:
collectionView.delegate = self
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(collectionView.frame.size.width, collectionView.frame.size.height)
}
It's also possible that when you have a collectionView in a UITableViewCell this cell actually resizes the collectionView height to 0.
In my case, the collectionView is showing an array of UIImageView to display some image gallery in each cell.
If the cell is not supposed to show any images (text without image for example), the collectionView is hidden.
The collectionView, if put within a StackView might need to have a height constraint and to avoid conflicts, you might need to set it to priority 999.
Then, when you hide the collectionView, it will actually try to set the height to 0.
The collectionView can still contain images in it and that will trigger the error.
SOLUTION: empty your datasource and reload the collectionView when you configure your cell.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
let cellsize = CGSize(width: (CollectionView.bounds.size.width/2) - 12, height:(CollectionView.bounds.size.height/3) - 20)
return cellsize
}
here CollectionView is outlet of UICollectionView
I have spent 3 days of searching and reading. In my IOS app I have a custom Cell for UICollectionView with xib File. In this custom cell I have 3 labels, images and buttons. 1 label from it is in the middle and always gets different sizes because it's multiline.
So I must calculate all items height. It's not the problem but the problem is only with this one multiline label. So I want calculate the size for each cell and get the size for sizeForItemAtIndexPath in UICollectionViewController.
After 3 days I think I cannot access this. Can I do this from the custom cell class? If yes, how? If I can access it in UICollectionViewController, how?
//MARK: - CollectionView Flow Layout
extension PhotosCollectionViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let viewSize = super.view.bounds.size
let spacing: CGFloat = 0.5
let width = (viewSize.width) - spacing - 10
/*
Here I need my cell with label name "Beschreibungstext"
to calculate the hight from the label with help from rectangle.
return CGSize(width: width, height: customhight)
*/
return CGSize(width: width, height: CGFloat(400))
}
}
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)