UICollection View causes "UICollectionViewFlowLayoutBreakForInvalidSizes" on smaller devices - ios

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

Related

How to increase the UICollectionview Cell Size equal to Screen Size in swift 4.2?

How to increase the UICollectionView Cell width & height equal to screen in swift 4.2 ?
Provided your UICollectionViewCell has a subview of UIImageView that is anchored to fill it completely(i.e. UIImageView is anchored UICollectionViewCell in top, bottom, left and right manner).
The following method can be used to size the UICollectionViewCell in accordance to the screen screen holding it, this will automatically result in the UIImageView being as big as the screen itself.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return self.collectionView.bounds.size //to ensure that this works as intended set self.collectionView size be the screen size.
}
Please note that the UICollectionView is big enough to hold the size of the UICollectionViewCell that you seek.
You need to change your cell size to fit the collectionview. Add this in viewDidLoad
if let flow = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flow.itemSize = self.collectionView.frame.size
}

Swift auto layout in UICollectionView with intrinsic height of cells

I'm having a hard time with auto layout and UICollectionView. I have a vertical UICollectionVIewFlowLayout that contains two different types of cells, as in the image below:
The SingleCard cell contains a subview with an image (UIImageView). The HorizList cell contains a subview which is a horizontal UICollectionView (a list of thumbnail photos). The heights of the two cell types are as follows:
SingleCard: the width of the cell shall be the same as the screen width. The height shall be dynamic to the height of the UIImageView (to keep proportions of the image and have it fill screen width no matter of device size)
HorizList: the height is constant, say 200 points.
My attempt, in the CollectionViewController is to do
override func viewDidLoad() {
...
let layout = collectionView?.collectionViewLayout as! UICollectionViewFlowLayout
layout.estimatedItemSize = CGSize(width: 200.0, height: 235.0)
}
This I do to get the two cell's intrinsic size to apply. Then I do not implement
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
at all.
My question is, what do I implement in
override var intrinsicContentSize: CGSize {
??
}
for the two different cell types? Maybe this is not the only thing I need to do, if so I'm grateful for additional hints.
Thanks!
/ola

UICollectionView: dynamic cell heights with UIStackView

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.

uicollectionview variable cell height

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)

Auto Layout size of CollectionViewCell in relation to CollectionView

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)
}

Resources