Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collection view grid last row appears differently despite equal widths - swift iOS

I've a fixed grid of collection view cells (UICollectionView) but the cells in the bottom row always appears with a slightly smaller width on screen. The frame size (or bounds) and calculated width used within collectionViewLayout: UICollectionViewLayout sizeForItemAt are the same for all rows.

enter image description here

        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
    let settings = currentContents[indexPath.item]
    let height = CGFloat(30)

    // https://stackoverflow.com/questions/54915227/uicollectionview-remove-space-between-cells-with-7-items-per-row
    var cellWidth = CGFloat()
    let availableWidth = collectionView.bounds.size.width
    print("available width", availableWidth)
    let minimumWidth = floor(availableWidth / collectionContents.cellsPerRow5)
    print("minmum width", minimumWidth)
    cellWidth = minimumWidth * settings.portion - 1
    print("cell width", cellWidth)

    return CGSize(width: cellWidth, height: height)
}

enter image description here

I'd like to get the bottom row to line up with the other rows, but can't imagine what is happening that is changing the widths after returning the value in the layout delegate method (or how to fix).

like image 389
DrWhat Avatar asked May 08 '19 07:05

DrWhat


1 Answers

UIKit does not like values with more than 2 decimals, round them or it will do it for you.

Here, UICollectionViewFlowLayout rounds your cell size and starts to fill the lines with an "interitemspacing" at least equal to the minimumInteritemSpacing you specified. On the last line, it used exactly the minimumInteritemSpacing value and doesn't fill entirely the line.

Fix it using better rounded values, giving the illusion that all is perfectly aligned.

I usually use those extensions:

extension CGFloat {
    func xx_rounded(_ rule: FloatingPointRoundingRule = .down, toDecimals decimals: Int = 2) -> CGFloat {
        let multiplier = CGFloat(pow(10.0, CGFloat(decimals)))
        return (self * multiplier).rounded(.down) / multiplier
    }
}

extension CGSize {
    func xx_rounded(rule: FloatingPointRoundingRule = .down, toDecimals: Int = 2) -> CGSize {
        return CGSize(
            width: width.xx_rounded(rule, toDecimals: toDecimals),
            height: height.xx_rounded(rule, toDecimals: toDecimals)
        )
    }
}

Change:

return CGSize(width: cellWidth, height: height)

to

return CGSize(width: cellWidth, height: height).xx_rounded()
like image 66
GaétanZ Avatar answered Nov 15 '22 10:11

GaétanZ