I have variable widths in my UICollectionView and in my sizeForItemAtIndexPath
function, I ultimately return a CGSize
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { var width: CGFloat = indexPath.row == 0 ? 37 : 20 // default paddings based on spacing in cell let font = UIFont.systemFont(ofSize: 14, weight: UIFontWeightRegular) if indexPath.row == 0 { width += listingsFilter.stringValue.width(withConstrainedHeight: 28, font: font) } else { let string = viewModel.valueRangeForButton[indexPath.row - 1] width += string.width(withConstrainedHeight: 28, font: font) } print(width) // some decimal e.g. 138.1239581 let w: CGFloat = width.rounded() // rounded e.g. 13 return CGSize(width: w, height: 28) }
If I stub the return value with a number for the width, say 128, rather than have a variable, the collection view respects UIEdgeInsets
. If I return the CGSize
with a variable for the width, UIEdgeInsets
gets ignored for every element but the first and the last.
I feel like I found some deep CoreGraphics
bug.
For the last two lines, if I change them to
let x: CGFloat = 128 // or 128.0, doesn't matter return CGSize(width: x, height: 28)
It still doesn't work. I've tried returning a CGSize
with the Int
initializer. I've tried casting to Ints and back to CGFloat
s. Nothing seems to work.
I've narrowed down the problem to this point. It doesn't have anything to do with the width
code above it (which is a string extension) or anything else.
Super weird. Anyone have experience with this?
Edit: Some images
Top one is with CGSize(width: w, height: 28)
where w
is a CGFloat
that could equal 128 or whatever other value. Bottom one is CGSize(width: 128, height: 28)
5 Answers
Answers 1
I had similar issue few months ago. As far as I remember it was really annoying trying to fix this (as in your case) but ridiculously simple.
You need to make sure that your class inherits from UICollectionViewDelegateFlowLayout
. It inherits from UICollectionViewDelegate
. That should make the trick.
Let me know if that works for you.
Answers 2
As i can understand from op's question
to move content within cell you need
func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return UIEdgeInsets(top: 15, left: 0, bottom: 15, right: 0) }
to move spaces between respective cells you need
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return 15 }
be sure that you confront the delegate UICollectionViewDelegateFlowLayout
Answers 3
Having never even tried to return a hardcoded value instead of a variable from sizeForItemAtIndexPath()
, I've always assumed that UIEdgeInsets
weren't even supposed to affect the spacing between the cells of a UICollectionView
, but only the spaces surrounding an entire section of cells. Looks like UIEdgeInsets
is not intended to affect the spacing between the cells, in which case Apple's bug is the reverse of what it seems to be -- that UIEdgeInsets
is oddly affecting the spacing between cells in one outlier case, when in fact it was never intended to do so in any scenario. This red herring had you reasonably assume that there must be a reliable and programmatic way to get UIEdgeInsets
to set the spacing between UICollectionView
cells. I have yet to find a programmatic way to do so. When I found the following storyboard solution I stopped looking for one.
After having spent a great deal of time tweaking UICollectionViews
to get them to look the way I want, I have found that the only reliable way to set the spacing between their cells is to use storyboard settings as follows:
In the storyboard, select your UICollectionView
, go to the attributes inspector, find the "Min Spacing" section, and type in the "For Cells" box the value you'd like (in your case, 10). The "For Cells" value is the horizontal spacing between cells. Do the same for the "For Lines" value, which is the vertical spacing between cells.
Answers 4
Please check the code below,
class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. collectionView?.delegate = self collectionView?.contentInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let x: CGFloat = 50 + CGFloat(arc4random_uniform(50)) // or 128.0, doesn't matter return CGSize(width: x, height: 28) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return UIEdgeInsets(top: 15, left: 0, bottom: 15, right: 0) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return 15 } override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 50 } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { return collectionView.dequeueReusableCell(withReuseIdentifier: "aaa", for: indexPath) } override func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 }
}
Answers 5
It's not related to UIEdgeInsets
, it relates to width
that you're calculating in code. When you set width: 128
, the width provided for your cells are bigger than what they actually need (let's say 90), hence depending on your UICollectionViewCell
's design, it might feel there is an UIEdgeInsets
for each cell.
To solve your problem:
If you're using IB, you can set cellSpacing
and lineSpacing
from IB, other-wise you have to implement UICollectionViewDelegateFlowLayout
0 comments:
Post a Comment