37

I want to use UICollectionView to display a horizontal scrollable list of thumbnails, however, I want this to be a single row. On another view, I'm displaying the thumbnails in a UICollectionView but this one scrolls vertically and it's in a single column.

Currently, the way I'm doing this is by changing the size of the collection view to only be big enough for one item so it autowraps and works, but now I'm dealing with variable sized items so it doesn't work anymore.

How do you make a UICollectionView display one row or column?

Meet Doshi
  • 3,983
  • 10
  • 35
  • 76
William T.
  • 12,040
  • 3
  • 52
  • 51

10 Answers10

29

I am guessing you are using the built in Flow Layout. However, for your case, you should make a custom UICollectionView Layout. Make it subclass of UICollectionViewFlowLayout and add this code in the init method:

- (id)init {
    if ((self = [super init])) {
        self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
        self.minimumLineSpacing = 10000.0f;
    }
    return self; 
}

The minimumLineSpacing is the key to making it have one line here.

I hope this helps!

Nikola Kirev
  • 3,154
  • 3
  • 20
  • 30
  • do you know how this affects intrinsic content size? I am trying to implement a single row collection view as a slide up / pop over, and constrain it to its minimum intrinsic size for a single row. Thanks! – Chris Conover May 14 '14 at 23:52
  • 1
    In case anyone wonders why this isn't working for them and you set the class from inside a storyboard, try doing this inside of -(id)initWithCoder:(NSCoder *)aDecoder{} instead – bitwit Feb 18 '15 at 02:42
  • 9
    +1 - One note is that you don't need to subclass for this (at least I didn't). In initializing my collection view, I did `flow.minimumInterItemSpacing = CGFloat_Max;` – Logan Jun 26 '15 at 19:56
  • I think `self.scrollDirection = UICollectionViewScrollDirectionHorizontal;` is the key to make it one line here. Can you please explain this one? – Kamaldeep singh Bhatia May 22 '18 at 12:41
12

I found that setting the minimumLineSpacing just made my scroll area really big and that setting the minimumInteritemSpacing=big accomplished keeping the items in one scrolling row or column, centered in the middle of the collectionview.

giorashc
  • 13,502
  • 3
  • 32
  • 69
sidekickr
  • 328
  • 3
  • 7
  • @giorashc, this is because it it laying out sideways, so a row would would be laid out vertically in this case. Hence setting a large value for minimumInterItemSpacing would require more space than the screen is tall, and it would break into multiple lines, horizontally. – Chris Conover May 14 '14 at 23:51
9

There isn't a real reason to subclass if you can trust the type cast.

((UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout).minimumLineSpacing = 1000.0f;

If you have your collectionView loaded from a nib you could also play around with the parameters in the Attributes and Size Inspectors.

Same goes for scroll direction.

atreat
  • 3,713
  • 1
  • 26
  • 33
6

When you init your UICollectionView pass the following UICollectionViewFlowLayout in the init parameter.

Note: You do not need to create a subclass of UICollectionViewFlowLayout

var collectionViewFlowControl = UICollectionViewFlowLayout()
collectionViewFlowControl.scrollDirection = UICollectionViewScrollDirection.Horizontal

For 0 px Cell Spacing:

collectionViewFlowControl.minimumInteritemSpacing = 0;
collectionViewFlowControl.minimumLineSpacing = 0;

collectionView = UICollectionView(frame: CGRectMake(0, 0, self.view.frame.size.width, 120), collectionViewLayout: collectionViewFlowControl)
Meet Doshi
  • 3,983
  • 10
  • 35
  • 76
Ilker Baltaci
  • 10,869
  • 6
  • 57
  • 73
6

In iOS 13, thanks to UICollectionViewCompositionalLayout, this is trivial, because you can specify the number of cells per column in a horizontal layout, or the number of cells per row in a vertical layout. Here's a simple example for a single horizontally scrolling, vertically centered row of cells:

func layout() -> UICollectionViewCompositionalLayout {
    let itemSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1),
        heightDimension: .fractionalHeight(1))
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    let groupSize = NSCollectionLayoutSize(
        widthDimension: .absolute(75),
        heightDimension: .absolute(75))
    let group = NSCollectionLayoutGroup.vertical(
        layoutSize: groupSize, subitem: item, count: 1) // *
    group.edgeSpacing = NSCollectionLayoutEdgeSpacing(
        leading: nil, top: .flexible(0),
        trailing: nil, bottom: .flexible(0))
    let section = NSCollectionLayoutSection(group: group)
    section.interGroupSpacing = 65
    let config = UICollectionViewCompositionalLayoutConfiguration()
    config.scrollDirection = .horizontal
    let layout = UICollectionViewCompositionalLayout(
        section: section, configuration:config)
    return layout
}

Set your collection view's collectionViewLayout to the output of that function call and you're all set.

matt
  • 447,615
  • 74
  • 748
  • 977
  • 1
    Thank-you for this. For anyone needing to know how to plug this in, add the function to the view controller and set your collection view like this: yourCollection.collectionViewLayout = layout() – Marcy Jul 28 '20 at 02:17
  • 1
    @Marcy Good addition, thanks. I'll just add a clarifying sentence too. – matt Jul 28 '20 at 02:19
2

For Swift 3

let flowLayout = UICollectionViewFlowLayout()
flowLayout.itemSize = CGSize(width: UIScreen.main.bounds.width/2-10, height: 190)
flowLayout.sectionInset = UIEdgeInsetsMake(0, 5, 0, 5)
flowLayout.scrollDirection = UICollectionViewScrollDirection.horizontal
flowLayout.minimumInteritemSpacing = 0.0
self.gCollectionView.collectionViewLayout = flowLayout
Azhar
  • 19,214
  • 38
  • 134
  • 202
1

This worked for me to get a single row of cells regardless of the height of the CollectionView. Set the Min spacing for cells to 1000

Storyboard UICollectionView

Jiebe
  • 329
  • 1
  • 4
  • 10
1

I think the documentation is the best :

minimumLineSpacingForSectionAt:

For a vertically scrolling grid, this value represents the minimum spacing between successive rows. For a horizontally scrolling grid, this value represents the minimum spacing between successive columns. This spacing is not applied to the space between the header and the first line or between the last line and the footer.

minimumInteritemSpacingForSectionAt:

For a vertically scrolling grid, this value represents the minimum spacing between items in the same row. For a horizontally scrolling grid, this value represents the minimum spacing between items in the same column. This spacing is used to compute how many items can fit in a single line, but after the number of items is determined, the actual spacing may possibly be adjusted upward.

So if your scrollDirection is horizontal, you need to set minimumInteritemSpacingForSectionAt to CGFloat.greatestFiniteMagnitude and if your scrollDirection is vertical, you need to set minimumLineSpacingForSectionAt to CGFloat.greatestFiniteMagnitude for getting one row collectionView

andesta.erfan
  • 856
  • 1
  • 9
  • 28
  • Except you should set `minimumInteritemSpacing` (or use `minimumInteritemSpacingForSectionAt`) regardless of whether it scrolls horizontally: as you quote, "For a vertically scrolling grid, this value represents the minimum spacing between items in the same row". – androidguy Jul 18 '20 at 21:43
  • @androidguy I just copied apple documentation not mine! – andesta.erfan Jul 19 '20 at 07:47
0
 UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
 [layout setScrollDirection:UICollectionViewScrollDirectionHorizontal];

 layout.minimumInteritemSpacing = 0;
 layout.minimumLineSpacing = 0;


 blendEffectCollectionView=[[UICollectionView alloc] initWithFrame:CGRectMake(0, 95, 320, 60) collectionViewLayout:layout];
 [blendEffectCollectionView setDataSource:self];
 [blendEffectCollectionView setDelegate:self];
 [blendEffectCollectionView registerClass:[BlendModeCell class] forCellWithReuseIdentifier:@"cellIdentifier"];
 [blendEffectCollectionView setBackgroundColor:[UIColor clearColor]];

 [bottomActivityView addSubview:blendEffectCollectionView];
Ajjjjjjjj
  • 596
  • 3
  • 12
0

I wanted one column, left aligned and the other answers didn't help me.

So here is my very simple solution for one column, left aligned:

class GCOneColumnLeftAlignedFlowLayout: UICollectionViewFlowLayout {
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributes = super.layoutAttributesForElements(in: rect)

        var y: CGFloat = sectionInset.top
        attributes?.forEach { layoutAttribute in
            layoutAttribute.frame.origin.x = sectionInset.left
            layoutAttribute.frame.origin.y = y
            y = y + layoutAttribute.frame.height + minimumLineSpacing
        }

        return attributes
    }
}

you also should set something like

estimatedItemSize = CGSize(width: 120, height: 40)

Don't forget to remove the line

minimumLineSpacing = big number

From the other solutions.

Hope this is helpful because it's a completely different approach compared to the other answers.

Gerd Castan
  • 5,141
  • 3
  • 35
  • 70