In the flow layout section of this UICollectionView guide in Apple documentation, under "Specifying the Space Between Items and Lines", we read:
During layout, the flow layout object adds items to the current line until there is not enough space left to fit an entire item. If the line is just big enough to fit an integral number of items with no extra space, then the space between the items would be equal to the minimum spacing. If there is extra space at the end of the line, the layout object increases the interitem spacing until the items fit evenly within the line boundaries, as shown in Figure 3-3. Increasing the spacing improves the overall look of the items and prevents large gaps at the end of each line.
(emphasis mine)
But this does not seem to be true. As of iOS 12.4 and iOS 13 beta 7, if you make a vertically scrolling collection view with small cells that don't fill the available width, the inter-item spacing does not expand from the minimum to fill the width. See this example with two sections of 5 items each, 20pt x 20pt, with minimum item spacing, minimum line spacing, and section inset all at 4 pt:
Is the documentation outdated? Or might I be doing something weird? This is a sample app that consists of nothing but a collection view whose delegate / data source uses vanilla UICollectionViewCells that just have a background color, and implements the sizing methods mentioned above, and nothing else.
Code:
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"Cell"];
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 2;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 5;
}
-(__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
cell.backgroundColor = [UIColor lightGrayColor];
return cell;
}
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(20, 20);
}
-(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
return UIEdgeInsetsMake(4, 4, 4, 4);
}
-(CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
return 4;
}
-(CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
return 4;
}
Related
This question already has answers here:
Left Align Cells in UICollectionView
(24 answers)
Closed 5 years ago.
How to remove space between two cells in UiCollectionView? Min spacing is not working.
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
if (collectionView.tag==101)
{
return CGSizeMake((_collView.frame.size.width),(_collView.frame.size.height));
}
else
{
return CGSizeMake((_collView2.frame.size.width/3)-20,300);
}
}
But this is not working.
*
*
Set Min Spacing for cells and for Lines to Zero
and replace your code by
- (CGSize)collectionView:(UICollectionView *)collectionView layout: (UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath: (NSIndexPath *)indexPath
{
if (collectionView.tag==101)
{
return CGSizeMake((_collView.frame.size.width),(_collView.frame.size.height));
}
else
{
return CGSizeMake((_collView2.frame.size.width/3),300);
}
}
Use UICollectionViewFlowLayout for those purposes:
UICollectionViewFlowLayout *flow = [[UICollectionViewFlowLayout alloc] init];
flow.itemSize = CGSizeMake(cellWidth, cellHeight);
flow.scrollDirection = UICollectionViewScrollDirectionHorizontal;
flow.minimumInteritemSpacing = 0;
flow.minimumLineSpacing = 0;
This happens because of how UICollectionviewFlowLayout works. You could provide a custom collectionview layout:
Left Align Cells in UICollectionView
Or use a 3d party solution:
https://github.com/mokagio/UICollectionViewLeftAlignedLayout
use this code, based on your design adjust the width and height.
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath: (NSIndexPath *)indexPath {
CGSize s = CGSizeMake([[UIScreen mainScreen] bounds].size.width/2-10,
[[UIScreen mainScreen] bounds].size.height/2.9-7);
return s;
}
bellow code adjust the edges of collection view
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(2, 6,2, 6);
}
Use both delegates and change the width and height
A method called minimumInteritemSpacingForSectionAtIndex from UICollectionViewFlowLayoutDelegate could help you. The minimunIteritemSpace set the space beetween items horizontally, for example try this code:
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionView *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
return 0; // This is the minimum inter item spacing, can be more
}
Also you can use the edgeInsets to change the space beetween items.
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
return 10.0;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
return 10.0;
}
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
// return UIEdgeInsetsMake(0,8,0,8); // top, left, bottom, right
return UIEdgeInsetsMake(20,20,20,20); // top, left, bottom, right
}
I'm using a collection view inside of a static table view cell and I'm using autolayout to define the collection view's frame. Also, I'm using FlowLayout as the collectionViewLayout.
The problem:
The behaviour is just as expected for the iPhone 4s and 5 screen. (same width)
Once the width changes (iPhone 6 and 6 Plus), the collection view resizes properly, but the content keeps it size.
This happens both for the cells and for the sections.
Extra info:
I'm setting collectionViewLayout minimumInteritemSpacingForSectionAtIndex to zero.
The width of the cells is always being set to tableView.frame.size.width / 2
Where I configure the layout:
CGFloat headerHeight = 52;
collectionViewLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);
collectionViewLayout.headerReferenceSize = CGSizeMake(self.width, headerHeight);
This is what it looks now. The green part represents the UICollectionView. Black and blue the cells. The dividing lines should be resizing and the cells should be divided in 2 columns.
From the code you posted, you are just setting the size for the header but I suppose you also have something like
collectionViewLayout.itemSize = CGSizeMake(self.collectionView.frame.width/2, height);
The important part is in which method you configure this, because if you do it in viewDidLoad the views are not correctly layouted yet so the width would just be the one set in the storyboard, no matter on which device you run your app.
The solution would be to place the code above in viewWillLayoutSubviews
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
collectionViewLayout.itemSize = CGSizeMake(self.collectionView.frame.width/2, height);
collectionViewLayout.headerReferenceSize = CGSizeMake(self.collectionView.frame.width/2, height);
}
Another way would be to implement the flowLayout delegate method:
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return cCGSizeMake(collectionView.frame.width/2, CUSTOM_HEIGHT);
}
// The same for the header
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
return cCGSizeMake(collectionView.frame.width/2, CUSTOM_HEIGHT);
}
Make sure you set the delegate for the collectionView
Let me know how it goes :)
Update
For the cells, add these methods too:
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return CGRectZero;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
return 0;
}
As for the sectionHeader, maybe you need to check the constraints you have there, for the separator for example.
Be sure to use all the following for precise styling:
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView;
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section;
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
-(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
-(CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;
-(CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
To change the cell content size, use this:
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake((kScreenWidth/2-8), 44); // Use your own X and Y sizes
}
I am stuck in frame of uicollectionview cell frame
what I want to do is,
in landscape mode of iPad I want to show 3 cells,
and in portrait 2 cells.
when I write the code of frame in method - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
its not affecting the size of frame.
I checked by applying debugger whether its coming in this method or not.
but its coming and returning the size.
by default I have to launch my application in landscape and then if required in portrait.
Here is how its showing in landscape
Here how this cells looking in portrait
Here is my code for frame
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
//return [self.cellSizes[indexPath.item] CGSizeValue];
if (UIDeviceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]))
{
return CGSizeMake(100, 100);
}
return CGSizeMake(200, 200);
}
What ever value I pass, frame is not at all changing.
I didn't passed any other frame value any where.
I want to give hardcoded value to cell frame first then will go for dynamic.
but I'm stuck at the first place only.
Can any one please help me.
Thanks in advance.
i faced similiar problem so basically you should implement UICollectionViewDelegateFlowLayout in your view controller otherwise design a custom collectionviewcell and register in your view controller , it will definitely work . Try it .
in my case
i added xib by subclassing UICollectionViewCell and designed it accordingly and it works so you may try .
For dynamic collection view cell you can use collection view methods like this.
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection: (NSInteger)section{
return [_elements count];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIdentifier = #"cell";
MosaicCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
MosaicData *data = [_elements objectAtIndex:indexPath.row];
cell.mosaicData = data;
float randomWhite = (arc4random() % 40 + 10) / 255.0;
cell.backgroundColor = [UIColor colorWithWhite:randomWhite alpha:1];
return cell;
}
For details you can see this example. This is an perfect example of your need
https://github.com/betzerra/MosaicLayout
You need to reload the collection view when orientation will get change. so the CGRect size method will be called for the cell to recreate cell.
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
// do something after rotation
[_collectView reloadData];
}
Add this method and reload your collection view so - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath will get called and size will get changed.
Thanks
I have a basic collection view with vertical scrolling, sections and variable amount of cells in each section. I need to adjust the width of each cell to fill available width of the view up to 4 cells, when I need to start next row of cells.
If a section has 1, 2, 3 or 4 cells, their width should be 1/1, 1/2, 1/3 or 1/4 of the view's width respectively. Sections with more then 4 cells should stretch 4 cells in one row and continue on the other row.
Do I need to subclass UICollectionViewFlowLayout for this or can I handle it in UICollectionViewController class and how?
You should work with below two methods to change the column in one row
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
return UIEdgeInsetsMake(5,5,0,5);
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
return CGSizeMake(75, 75);
}
You have to play with UICollection view Delegate methods to give a custom design for CollectionCell
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
return 2.0;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
return 2.0;
}
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(0,5,0,5);
}
and size you can set in Frame File inspector
I am attempting to lay my UICollectionView out such that it is a 2x3 grid per page. Here is what I am getting so far:
As you can see, the next "page" is peeping in from the right, which I don't want. I figured if I know my width is always going to be 320pts (iPhone only), then I would want my cells to be 130pts in width with 15pts on either side.
Code for layout delegate:
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(130, 130);
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(20.0f, 15.0f, 20.0f, 15.0f);
}
And also cellForItemAtIndexPath:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath{
CustCollectionViewCell *cell =
[collectionView dequeueReusableCellWithReuseIdentifier:kReuseID
forIndexPath:indexPath];
//Set up list name label
cell.LblListName.font = [self getDefaultFontWithSize:15.0f];
cell.LblListName.textColor = fontAndTertiaryColor;
cell.LblListName.textAlignment = NSTextAlignmentCenter;
cell.LblListName.text = #"Failtron";
//cell.LblListName.adjustsFontSizeToFitWidth = YES;
cell.backgroundColor = secondaryColor;
cell.parallaxIntensity = kParallaxMotion;
[Utilities createViewBorder:cell];
return cell;
}
I thought an inset would act like padding in CSS but I believe I'm off the mark with that one, I should also note that in the UIStoryboard my layout is set to flow. Please let me know if I am missing any relevant code, this is my first attempt with a UICollectionView. I would like this to scroll horizontally as well.
Ok, there are tons of ways to manipulate layouts for a UICollectionView, but sounds like since you are using "flow" you can just implement <UICollectionViewDelegateFlowLayout> in your .h and then use the following methods to tweak your layout to your liking:
#pragma mark – UICollectionViewDelegateFlowLayout
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
// Size of the cell
CGSize retval = CGSizeMake(130,130);
return retval;
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
insetForSectionAtIndex:(NSInteger)section {
//top, left, bottom, right
return UIEdgeInsetsMake(0, 20, 0, 20);
}
- (CGFloat)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
//Minimum spacing between cells
CGFloat interimSpacing = 0.0f;
return interimSpacing;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
minimumLineSpacingForSectionAtIndex:(NSInteger)section {
// Minimum spacing between lines. In your case,
this is the distance between columns
// If your scroll direction was vertical,
this would be the distance between rows
CGFloat lineSpacing = 20.0f;
return lineSpacing;
}
I've prepopulated this code with some values that should be pretty darn close to what you wanted to accomplish.
Alternatively, all of this can be done in Interface Builder by selecting the UICollectionView and adjusting the values in the Size Inspector, albeit more confusing in my opinion.