I'm likely going about this in the completely wrong way since what I try to do should be simple... I have a view component which contains a few subviews, one of them being a UICollectionView. I layout the subviews using SnapKit. The UICollectionView is a flowlayout of buttons, spanning over 0-n rows, number of buttons is known from the beginning.
When I configure the constraints via SnapKit the height of the Collection is 0, since no items are present (cellForItemAt still not called).
How would I go about to get the height of the collection to be dynamically set in SnapKit? Or is there a better way to get the very basic flowlayout I'm looking for (with dynamic heigth-adjustement)?
(Note: The code is a bit simplified to only show relevant parts)
class CategoryList: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
var categories : [String]?
var buttons = [BorderedButton]()
private(set) lazy var collectionView: UICollectionView = {
var flowLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: flowLayout)
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "collectionCell")
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = UIColor.cyan
return collectionView
}()
override func loadView() {
super.loadView()
self.view.addSubview(collectionView)
}
override func viewDidLoad() {
super.viewDidLoad()
setupConstraints()
}
override func viewDidAppear(_ animated: Bool) {
print("123: \(collectionView.contentSize.height)")
//prints 47 for this particular example, however doing snp.makeConstraints
//(or remakeConstraints) from here doesn't make any difference
//setupConstraints()
}
func configure(categories:[String]) {
self.categories = categories
for category in categories {
let button = BorderedButton()
button.setTitle(category, for: .normal)
buttons.append(button)
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return buttons.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath)
cell.addSubview(buttons[indexPath.row])
//prints 47 for this particular example, however doing snp.makeConstraints
//(or remakeConstraints) from here doesn't make any difference
print("XYZ: \(collectionView.contentSize.height)")
return cell
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize
{
return buttons[indexPath.row].intrinsicContentSize
}
private func setupConstraints() {
collectionView.snp.makeConstraints { (make) in
make.left.top.equalToSuperview()
make.width.equalTo(300)
print("ABC: \(collectionView.contentSize.height)")
//this does not work since height=0
make.height.equalTo(collectionView.contentSize.height)
//this works fine
make.height.equalTo(47)
}
}
}
Thanks!
Thanks for the reply below. It indeed works, and great you could get the question even though I missed to include half of the code :-) Below is the missing piece, it is the class using the CategoryList. With your help I get the CategoryList to expand dynamically to the correct height, which is excellent. However, it messes up the placement of the surrounding views (view1 and view2 below). I suspect it has something to do with the
make.height.equalTo(categoryList.collectionView.snp.height)
line, maybe it should just reference the collectionView constraint property instead, but I have no idea how that should (or even could) be done?
class PlaylistDetailsHeaderView : UITableViewCell {
private var categoryList:CategoryList
private(set) lazy var view1: UIView = {
let view = UIView()
return view
}()
private(set) lazy var view2: UIView = {
let view = UIView()
return view
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
self.categoryList = CategoryList()
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(view1)
self.contentView.addSubview(view2)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure() {
let categories = ["alfa", "beta", "gamma", "fkdiii", "keoooe", "dfdsje", "jkjfldsjflsd", "alfa", "beta", "gamma", "fkdiii", "keoooe", "dfdsje", "jkjfldsjflsd"]
categoryList.configure(categories: categories)
self.contentView.addSubview(categoryList.view)
setupConstraints()
}
private func setupConstraints() {
view1.snp.makeConstraints { make in
make.left.top.equalToSuperview()
}
categoryList.view.snp.makeConstraints{ make in
make.left.equalToSuperview()
make.top.equalTo(view1.snp.bottom).offset(20)
make.width.equalTo(300)
make.height.equalTo(categoryList.collectionView.snp.height)
}
view2.snp.makeConstraints { make in
make.top.equalTo(categoryList.view.snp.bottom).offset(20)
}
}
}
If i understand what you meant this is how i think you should do it.
First you need to store the height somewhere. That will be defaulted to 0, or in your case 47.
var heightConstraint: Constraint?
Then you will set the constraints in snapkit as normal, but when you set the height constraint you store it inside the previously created variable. Don't forget to add ".constraint".
heightConstraint = make.height.equalTo(47).constraint
Next step is when the collectionView is populated, you can use the heightConstraint again (if you made the variable reachable)
heightConstraint.updateOffset(collectionView.contentSize.height)
Vollan's answer is quite close. Just ensure that you're on the main thread when updating the constraint. So summing up:
var collectionViewHeightConstraint: Constraint? // on top as class property
Then inside the snapkit block:
collectionView.snp.makeConstraints { make in
make.top.equalToSuperview() //example
collectionViewHeightConstraint = make.height.equalTo(self.collectionView.contentSize.height).constraint
}
And last but not least, when data gets updated, don't forget:
DispatchQueue.main.async {
self.collectionViewHeightConstraint?.update(offset: self.collectionView.contentSize.height)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With