I've run into this problem from time to time and still haven't come up with a satisfying solution. I am trying to create a complex scrollable view. Consider the following example:
[UIView 1]
[UIView 2]
[UIView 3]
[UICollectionView]
[UIView 4]
In this case, any of the four UIViews can be hidden or visible. The UICollectionView should not scroll, rather it should show ALL of the cells. All five of these views should be in a UIScrollView that scrolls all of them.
I have yet to find the secret sauce to make this work.
Option 1] Lay stuff out completely manually in code. I know this will work, but it is very inconvenient as it impacts the subviews, which are complex in their own right.
Option 2] Each of the five views is in a UICollectionView cell, and UICollectionView handles sizing and scrolling. I likewise know this will work, but it seems like a lot of overhead to accomplish this.
Option 3] Wrap the five views in a UIStackView. This seems to be the most natural solution to this problem, but it relies on being able to do three things at the same time that I have yet to be able to accomplish:
Set the height of the stack view based on the height of the [visible] five views. I have tried to accomplish this with a height constraint linked from interface builder into the code.
Setup the collection view to have a variable height based on the number of cells it contains.
Set the UIScrollView content to have the same height as the stack view.
Up to this point, I haven't found the right set of constraints to make all of these things happen, and it is starting to make me think a UIScrollView with a UIStackView in it is not a viable solution to this problem. With everything I have tried, I either break the collection view's ability to display all of the cells or I cannot access the entire stack view within the scroll view (IE, it gets cut off before the bottom).
Thoughts on the correct strategy to solve such a problem? (thanks in advance)
I would definitely go with Option 3. I faced a similar but different hierarchy (a form with UIImageView, UIStackView, UIButton, and inside the stack view is two text fields, one text view, and a table view.
I gave the text view a height constraint greater than a certain value, then used a subclass of UITableView to make its height dynamic based on the content. I was able to create a similar subclass for UICollectionView that you might want to use to make your collection view height dynamic:
import UIKit
class DynamicHeightCollectionView: UICollectionView {
override var contentSize: CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
layoutIfNeeded()
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
}
override func layoutSubviews() {
super.layoutSubviews()
}
}
then from storyboard, make sure you select the collection view, go to the Size inspector, and set the Intrinsic Size to Placeholder to avoid storyboard warnings.
This should solve all layout issues in your storyboard and hopefully work.