I have a UICollectionViewCell
that has a UICollectionView
with UICollectionViewFlowLayout
inside. I am making the cell the delegate and the datasource of the collection view. I am conforming to UICollectionViewDelegateFlowLayout
and implementing collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
(and returning the correct size).
However, when I call layout.itemSize.height
I get the default height, 50.
class MyCell: UICollectionViewCell {
fileprivate lazy var collectionView: UICollectionView = {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(SomeOtherCell.self, forCellWithReuseIdentifier: SomeOtherCell.reuseIdentifier)
collectionView.showsHorizontalScrollIndicator = false
collectionView.showsVerticalScrollIndicator = false
return collectionView
}()
fileprivate lazy var layout: UICollectionViewFlowLayout? = {
let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.scrollDirection = .horizontal
layout?.sectionInset = UIEdgeInsets.zero
return layout
}()
override func sizeThatFits(_ size: CGSize) -> CGSize {
collectionView.frame = CGRect(x: layoutMargins.left, y: origin.y, width: width, height: layout.itemSize.height)
// layout.itemSize.height is always 50
}
}
extension MyCell: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
guard tags.count != 0 else {
return .zero
}
let tag = Tag(text: tags[indexPath.item], index: indexPath.item)
placeholderCell.configure(with: tag)
let size = placeholderCell.getFrame(at: .zero, fitting: placeholderCell.width, alignedBy: semanticContentAttribute, apply: false).size
collectionViewWidth += size.width
// this size is always correct, that's what I want when I call layout.itemSize.height
return size
}
}
A layout object that organizes items into a grid with optional header and footer views for each section.
class MyLayout: UICollectionViewFlowLayout { override func prepare() { super. prepare() guard let collectionView = collectionView else { return } itemSize = CGSize(width: ..., height: ...) } }
A compositional layout is a type of collection view layout. It's designed to be composable, flexible, and fast, letting you build any kind of visual arrangement for your content by combining — or compositing — each smaller component into a full layout.
According to Apple documents for UICollectionViewFlowLayout's itemSize
If the delegate does not implement the collectionView(_:layout:sizeForItemAt:) method, the flow layout uses the value in this property to set the size of each cell. This results in cells that all have the same size.
The default size value is (50.0, 50.0).
It says clearly that itemSize
is only used when the delegate does not implement the collectionView(_:layout:sizeForItemAt:) method and no words mention that itemSize
will return value from collectionView(_:layout:sizeForItemAt:)
. They are 2 different values. That's why itemSize
won't change despite sizeForItemAt
is implemented
So it makes sense if layout.itemSize.height
has default value (50) when you use it.
If all of cells on your collectionView
have same size, I suggest to set a value for itemSize
and don't implement collectionView(_:layout:sizeForItemAt:)
method (Remove extension for UICollectionViewDelegateFlowLayout
).
class MyCell: UICollectionViewCell {
fileprivate lazy var collectionView: UICollectionView = {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
// collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(SomeOtherCell.self, forCellWithReuseIdentifier: SomeOtherCell.reuseIdentifier)
collectionView.showsHorizontalScrollIndicator = false
collectionView.showsVerticalScrollIndicator = false
return collectionView
}()
fileprivate lazy var layout: UICollectionViewFlowLayout? = {
let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.scrollDirection = .horizontal
layout?.sectionInset = UIEdgeInsets.zero
// Calculate and change |itemSize| here
layout?.itemSize = YOUR_CELL_SIZE_AFTER_CALCULATED
return layout
}()
override func sizeThatFits(_ size: CGSize) -> CGSize {
collectionView.frame = CGRect(x: layoutMargins.left, y: origin.y, width: width, height: layout.itemSize.height)
// layout.itemSize.height is |YOUR_CELL_SIZE_AFTER_CALCULATED.height|
}
}
If collectionView
has different size for each cell, you need to use UICollectionView's layoutAttributesForItem(at:) method. Get UICollectionViewLayoutAttributes at an IndexPath and use layoutAttributes.size to calculate
layoutAttributes.size is value returned from collectionView(_:layout:sizeForItemAt:)
let indexPath = // IndexPath you need to use to calculate
let layoutAttributes : UICollectionViewLayoutAttributes? = collectionView.layoutAttributesForItem(at: indexPath)
// Use this value to calculate |collectionView| frame
print(layoutAttributes?.size)
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