Question

After much fiddling I'm beginning to suspect that there's a bug in the spacing code of the UICollectionViewFlowLayout.

The problem is simple enough: I have five buttons I want to show on screen and I want the spacing between buttons to be 1 pt(2 pixels on retina display). To achieve this I have set the minimumInteritemSpacing to 1.0 and the cell width to 159.5 pt such that two cells will add up to 319 pt leaving a single point for the space. The problem appears when I want the third button to be the full width of the view because the flow layout causes the spacing to be 0.5 pt despite having been told to keep a minimum of 1.0. (see screenshot 1)

right layout wrong spacing

The code for setting up the spacing happens in

-(void)viewDidLoad
{
    UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)[[self collectionView] collectionViewLayout];
    [flowLayout setMinimumInteritemSpacing:1.0];
    [flowLayout setMinimumLineSpacing:1.0];        
}

and the sizing of the cells in

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row == 2) {
        return CGSizeMake(320 ,151);
    }
    return CGSizeMake(159.5, 151);
}

If, on the other hand, I don't make the third button full width the spacing behaves as expected though it shouldn't affect the inter item spacing of a different row. (screenshot 2)

right spacing wrong layout

It should be noted that the CollectionView, CollectionViewCell and UILabel are set up in the storyboard and that I've already made sure the frame of the label visible in the screenshots is less than the cell's width and I have also tried removing the label altogether but with the same result.

I can't see why this is happening unless it is a bug in the flow layout code and I was looking for a sanity check here before submitting this as a bug.

Was it helpful?

Solution

As mentioned in the comments, a combination of having to describe the problem here and a bit of subsequent thought caused me to realize that although minimumInteritemSpacing is a CGFloat it doesn't make sense to pass it fractional values and its behaviour when you do so is undefined. This is because, on devices where there's a 1:1 point to pixel ratio, there is no such thing as half a pixel. It just so happened that in some cases it behaved as I expected and thus blinded me to the real (and rather obvious) problem.

Hopefully reading this can help others quickly realize the mistake.

OTHER TIPS

The solution is to divide the point value by the screen scale.

flowLayout.setMinimumInteritemSpacing = 1.0 / UIScreen.main.scale

The property takes a value in points, which is an abstraction of the screen pixels. From the Apple docs:

For Retina displays, the scale factor may be 3.0 or 2.0 and one point can represented by nine or four pixels, respectively. For standard-resolution displays, the scale factor is 1.0 and one point equals one pixel.

By dividing the point value by the screen scale you get a CGFloat value that is equal to 1 pixel on the device screen.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top