I have a side-scrolling collection view that contains two images. These images may be changed by the user during runtime. This change might cause a change of the collection view cell size, hence the collection view also need to adapt its height according to the height of the cells.
I've implemented the UICollectionViewDelegateFlowLayout protocol and the sizeForItemAt function which returns the proper cell size given the image.
I also have a height constraint outlet connected to my collection view which allows me to change its height, matching the height value of the chosen image.
It is all presented as you would expect when drawn to the screen, but I get the error "The behavior of the UICollectionViewFlowLayout is not defined because..", complaining that the height returned by sizeForItemAt exceeds my collection views default height (set in the storyboard).
It seems that I need to invalidate my collection view layout once I've altered its height constraint so that the collection view layout is updated with this new value before sizeForItemAt returns the cell sizes.
I've tried to call UICollectionViewLayouts invalidateLayout() right after updating the collection view height constraint, but that didn't help.
Any ideas to what I am doing wrong?
SOLVED
Ok, what I needed to do was to call view.layoutIfNeeded() for my view controller that holds the collection view right after I changed the collection views height constraint. Then the bounds for the collection view will be updated with the new height value so that the cells will fit.
Related
I have a content view (this content view I will use as a subclass of UICollectionViewCell content later) which has an UIStackView inside. Inside that stack view, I add some labels, view. And I calculate some conditions to make sure that when data of a label is empty, that label will be hidden and the stack view will auto change the height and vice versa, if they have data I'll show them and stack view update the height again.
Now I wanna know how can I make the content view height always follow the UIStackView height whenever it's changing.
If you already made your UIStackView to have a dynamic height following its children, then all you need is to add constraints between UIStackView and your content view and it will resize automatically as well.
Now, if you add that content view inside a UICollectionViewCell, then you must make sure to call UICollectionView.reloadData() whenever you change the content of your UIStackView. That way the collection view will recalculate the size and render the cell again. You also have the option to reload a single cell in the collection view if you have a way to determine its indexPath.
Note: Take care of CollectionViewFlowLayout and what size it dictates to the cell. It's recommended that you tell the flow layout the estimated height of your cell via layout.estimatedHeight.
I have an array of stack views with varying heights:
[Stack View A, Stack View B, Stack View C, Stack View D]
I want to display them in a collectionView. Every stackView goes into a different collectionView cell. I am currently using a flow layout to set the height based on what the view is in the collection viewController.
Would it be possible to abstract this so that the collection vc doesn't need to know anything about them? I can pass the view to be displayed in the cell and the cell's height is determined by the height of the stack view. Something like intrinsic content size, as opposed to setting it in the flow layout delegate sizeforitem method.
You could set the Estimated Size of the collectionView as "Automatic" as well as the cell's size and each cell will self size with Auto Layout constrained views.
As stated in the Xcode 11 Release Notes:
Cells in a UICollectionView can now self size with Auto Layout
constrained views in the canvas. To opt into the behavior for existing
collection views, enable “Automatic” for the collection view’s
estimated size, and “Automatic” for cell’s size from the Size
inspector. If deploying before iOS 13, you can activate self sizing
collection view cells by calling performBatchUpdates(_:completion:)
during viewDidLoad(). (45617083)
Let me preface by saying that I've spent probably 100 hours over the past few months searching Google and StackOverflow trying to answer this question. I've followed many tutorials and have not been able to solve my problem. Every one is a major disappointment and is disheartening.
Expectation
Reality
Problem
I have a UITableView that contains a UIView. The UIView has auto layout constraints to pin it 15pt off the edges of the cell, and then I give that a shadow and rounded corners. Inside that UIView, I have a few labels, then a UICollectionView, and a stack view.
My UICollectionView keeps having a height of 0 and I can't figure out how to make it have intrinsic size. The only way I can seem to make it display properly is to force a specific height to it via auto layout inside the storyboard (which is what I did to take this screenshot). Unfortunately, forcing a height like this is not realistic for 2 reasons:
The height varies based on the width of the device. Since the images are squares, the height is loosely (but not exactly) half the width of the device.
If there are no images, the height of the collection view should be 0.
It really seems like it should be possible to use the intrinsic content size of the flow layout here.
Storyboard
Constraints
I've got about 15pt of space between each of the labels, collection view, and stack view (so, the vertical space). The collection view prototype cell has an image view in it. The image view has top, bottom, trailing and leading space all set to 0 to the collection view cell, and also has a 1:1 ratio.
Collection View Layout
To achieve the layout of one large image and 4 small images, I'm using SNCollectionViewLayout.
Data Source / Delegate
In my controller, in viewDidLoad, I've set the estimatedRowHeight of the UITableView to 400, and I've set the rowHeight to UITableView.automaticDimension.
In the tableView cellForRowAt, I dequeue a reusable custom UITableViewCell class, set various outlets on the table view cell, and call reloadData() on the collection view for the table view cell. In the storyboard, I've set the table view cell as the collection view's data source and delegate.
In the table view cell's awakeFromNib function, I've got it setting the corner radius, shadow, and using the following code for the flow layout on the collection view (which you can see in their example):
let snCollectionViewLayout = SNCollectionViewLayout()
snCollectionViewLayout.fixedDivisionCount = 4
snCollectionViewLayout.delegate = self
snCollectionViewLayout.itemSpacing = 10
collectionView.collectionViewLayout = snCollectionViewLayout
I've also implemented the optional SNCollectionViewLayoutDelegate protocol by writing the following function:
func scaleForItem(inCollectionView collectionView: UICollectionView, withLayout layout: UICollectionViewLayout, atIndexPath indexPath: IndexPath) -> UInt {
if indexPath.row == 0 || collectionView.numberOfItems(inSection: indexPath.section) <= 2 {
return 2
} else {
return 1
}
}
This function is why the first image is twice the height and width as the rest.
Research
In trying to figure this out, I've checked the collectionView.collectionViewLayout.collectionViewContentSize inside the table view cell's cellForRowAt and willDisplay methods. In either case, it's (0,0).
I've also tried checking it in the collection view cell's cellForItemAt and willDisplay. Those log lines don't even print unless I add a height constraint to the collection view to force it tall, but then I don't have a good way to bring the height back down to what it should be, and any time I've tried, I got conflicting constraint warnings and really wonky stretched views (such as the stack view being totally squished).
In testing, I've forced a height constraint that's large enough for the collection view to fit, and when that happens, it renders correctly and the collection view content size is correct, so I know it's capable of keeping track (which can be seen here).
Does anyone have any insight into this? I'd be happy to provide more code if you have any specifics on things to look into. I tried to give the full picture, while keeping this as short as possible (I know it's long).
I feel like the issue has something to do with the collection view being inside a table view cell.
Thanks in advance.
Keep a height constraint connection in your table view cell.
#IBOutlet weak var collectionViewHeightConstraint: NSLayoutConstraint!
And update the height constraint value programatically as per height calculations
self. collectionViewHeightConstraint.constant = 100 // set it to 0 if nothing to display
For an iOS application I'm working on the following layout:
I'm trying to achieve this by embedding a tableview inside a collection view cell.
The height of the individual collectionView cells is dynamic (by setting the layouts' estimatedItemSize and using auto layout for the cells). The problem which I encountered is that I can't get the embedded tableView to size dynamically according to the given data.
Is there any way I can update the size of the tableView in the cell dynamically
For collection view or table view, auto-sizing will only work if you have provided enough constraint to calculate it's CGRect. In your case, you have table view inside collection view and table view's height can be anything as it can scroll the content.
Try to give table view height constraint then change constraint's value to table view's contentsize.height, Then it might work.
Maybe consider using UIStackView.
I am looking to make an image gallery. I want my images full screen but am not sure how this works with Auto Layout. I know there is a method to return the size of a cell but didn't know if I should just be getting the screen size and returning it or if there was an approach with using Auto Layout.
The cell size in a collection view (or table view for that matter) isn't set with auto layout. You should set the size of the cell will the UICollectionViewFlowLayout method, itemSize. You can set the size of the collection view itself in IB (or code) with constraints, so it will adjust for different screen sizes, then set your cell size to be the same as the collection view's size.