IOS UICollectionView custom cell order

I have a simple collection view with 5 elements. The default order for cells is as follows: enter image description here

I want to know if padding \ order of cells can be changed to get the result as follows:

enter image description here

+4
source share
4 answers

SOLUTION: I created a subclass of UICollectionView with the name "CenteringCollectionView" and a few section calculations that I did!

.H file:

#import <UIKit/UIKit.h>

@class CenteringCollectionView;
@protocol CenteringCollectionViewDelegate <NSObject>

-(void)collectionView:(CenteringCollectionView *)collectionView didDequeueReusableCell:(UICollectionViewCell *)cell indexPath:(NSIndexPath *)indexPath;

@end

@interface CenteringCollectionView : UICollectionView

@property (nonatomic, strong) NSMutableArray *dataSourceArr;
@property (nonatomic, weak) id<CenteringCollectionViewDelegate> delegateCenteringCollection;

@end

.M file:

#import "CenteringCollectionView.h"

@interface CenteringCollectionView () <UICollectionViewDelegate, UICollectionViewDataSource>

@property (nonatomic, assign) IBInspectable long elementsInRow;
@property (nonatomic, assign) long elementsInRowInitialValue;
@property (nonatomic) IBInspectable NSString *cellIdentifier;
@property (nonatomic) IBInspectable CGFloat cellRelativeSize; // 0..1
@property (nonatomic, assign) long numOfSections;
@property (nonatomic, assign) IBInspectable BOOL autoResize; // *** If we want auto resize - we need to set the height constraint of the collection view in size of 1 line only even if we have more than 1 line (section).
@property (nonatomic, assign)IBInspectable CGFloat heightMiddleSpacing;
@property (nonatomic, assign) long cellSize;
//@property (nonatomic, assign) CGFloat verticalTopInset;
@property (nonatomic, assign) CGFloat initialHeightConstraint;
@property (nonatomic, weak) NSLayoutConstraint *selfHeightConstraint;
@property (nonatomic, assign) CGFloat cellSpacing;
@property (nonatomic, assign) BOOL shouldReloadUIElements;

// UI IBInspectable

@property (nonatomic, weak) IBInspectable UIColor *runtimeColor;

@end

static long const maxElementsInRowDefault = 3;

@implementation CenteringCollectionView

-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super initWithCoder:aDecoder])
    {
        self.elementsInRow = maxElementsInRowDefault; // will get the default value if not stored value in storyboard
        self.elementsInRowInitialValue = self.elementsInRow;
        self.cellRelativeSize = 0.5;
        self.initialHeightConstraint = -1;
    }
    return self;
}

-(void)setDataSourceCount:(long)dataSourceCount
{
    if (dataSourceCount == _dataSourceCount)
    {
        return;
    }

    _dataSourceCount = dataSourceCount;
    self.shouldReloadUIElements = YES;
    self.elementsInRow = MIN(self.elementsInRowInitialValue, self.dataSourceCount);
    self.numOfSections = ceil((CGFloat)self.dataSourceCount / (CGFloat)self.elementsInRow);

    CGFloat selfHeight = [self handleAutoResizeAndReturnTheNewHeightIfNeeded];
    CGFloat selfWidth = CGRectGetWidth(self.frame);

    CGFloat cellWidth = (selfWidth / self.elementsInRow) * self.cellRelativeSize;
    CGFloat cellHeight = (selfHeight / self.numOfSections) * self.cellRelativeSize;

    self.cellSize = MIN(cellWidth, cellHeight);

    dispatch_async(dispatch_get_main_queue(), ^{

        [self setCollectionView];
        [self reloadData];
    });
}

-(void)awakeFromNib
{
    [super awakeFromNib];
    self.elementsInRowInitialValue = self.elementsInRow;
    [self handleUIelementsIBInspectable];
}

-(void)handleUIelementsIBInspectable
{
    if (self.runtimeColor)
    {
        [self setBackgroundColor:self.runtimeColor];
    }
}

-(CGFloat)handleAutoResizeAndReturnTheNewHeightIfNeeded
{
    if (self.autoResize)
    {
        for (NSLayoutConstraint *constraint in [self constraints])
        {
            if (constraint.firstAttribute == NSLayoutAttributeHeight)
            {
                if (self.initialHeightConstraint == -1) // not set yet
                {
                    self.initialHeightConstraint = constraint.constant;
                }

                if (!self.selfHeightConstraint)
                {
                    self.selfHeightConstraint = constraint;
                }


                CGFloat newHeight = self.initialHeightConstraint * self.numOfSections;
                constraint.constant = newHeight;

                if (self.bounds.size.height != newHeight)
                {
                    CGRect frame = self.bounds;
                    frame.size.height = newHeight;
                    [self setBounds:frame];
                }

                dispatch_async(dispatch_get_main_queue(), ^{
                    [self.superview layoutIfNeeded];
                    [self layoutIfNeeded];
                });

                return newHeight;
            }
        }
    }

    return CGRectGetHeight(self.frame);
}

-(long)numOfSpacesInRow
{
    return self.elementsInRow + 1;
}

-(long)numOfSpacesBetweenLines
{
    return self.numOfSections + 1;
}

-(void)setCellRelativeSize:(CGFloat)cellRelativeSize
{
    _cellRelativeSize = MAX(0, MIN(cellRelativeSize, 1));
}

-(void)setCollectionView
{
    [self reloadData];
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    CGFloat horizontalCellSpacing = ((CGRectGetWidth(self.frame) - (self.cellSize * self.elementsInRow)) / self.numOfSpacesInRow);
    CGFloat verticalCellSpacing = (CGRectGetHeight(self.frame) - (self.numOfSections * self.cellSize)) / self.numOfSpacesBetweenLines;

    self.cellSpacing = MAX(MIN(horizontalCellSpacing, verticalCellSpacing), 0);
    [layout setMinimumInteritemSpacing:self.cellSpacing];
    [layout setMinimumLineSpacing:self.cellSpacing];
    [layout setScrollDirection:UICollectionViewScrollDirectionVertical];
    [self setCollectionViewLayout:layout];

    self.showsVerticalScrollIndicator = NO;
    self.showsHorizontalScrollIndicator = NO;
    self.scrollEnabled = NO;

    if (!self.delegate)
    {
        self.delegate = self;
        self.dataSource = self;
    }
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return CGSizeMake(self.cellSize, self.cellSize);
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    BOOL isLastSection = (section == self.numOfSections - 1);
    if (isLastSection == NO)
    {
        return self.elementsInRow;
    }
    else
    {
        long numOfLeftItemsInLastRow = self.dataSourceCount % self.elementsInRow;
        if (numOfLeftItemsInLastRow == 0)
        {
            return self.elementsInRow;
        }
        else
        {
            return  numOfLeftItemsInLastRow;
        }
    }
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:self.cellIdentifier forIndexPath:indexPath];
    if ([self.delegateCenteringCollection respondsToSelector:@selector(collectionView:didDequeueReusableCell:indexPath:)])
    {
        [self.delegateCenteringCollection collectionView:self didDequeueReusableCell:cell indexPath:[self indexPathWithoutSectionsFrom:indexPath]];
    }
    return cell;
}

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    if ([self.delegateCenteringCollection respondsToSelector:@selector(collectionView:didSelectItemAtIndexPath:cell:)])
    {
        UICollectionViewCell *selectedCell = [collectionView cellForItemAtIndexPath:indexPath];
        [self.delegateCenteringCollection collectionView:self didSelectItemAtIndexPath:[self indexPathWithoutSectionsFrom:indexPath] cell:selectedCell];
    }
}

-(NSIndexPath *)indexPathWithoutSectionsFrom:(NSIndexPath *)indexPath
{
    long sectionNum = indexPath.section;
    long rowNum = sectionNum * self.elementsInRow + indexPath.row;
    NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:rowNum inSection:0];
    return newIndexPath;
}

-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return self.numOfSections;
}

-(void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.shouldReloadUIElements == NO)
        {
            return;
        }

        if (self.autoResize && self.selfHeightConstraint)
        {
            BOOL isTheFirstCellInTheLastSection = (indexPath.section == self.numOfSections - 1) && indexPath.row == 0;
            if (isTheFirstCellInTheLastSection)
            {
                CGFloat newHeight = CGRectGetMaxY(cell.frame) + self.cellSpacing;

                self.selfHeightConstraint.constant = newHeight;

                if (self.bounds.size.height != newHeight)
                {
                    CGRect frame = self.bounds;
                    frame.size.height = newHeight;
                    [self setBounds:frame];
                }

                [self.superview layoutIfNeeded];
                [self layoutIfNeeded];
            }
        }
    });
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
    if (self.shouldReloadUIElements == NO)
    {
        return collectionView.contentInset;
    }

    NSInteger cellsCount = [collectionView numberOfItemsInSection:section];
    CGFloat horizontalInset = (collectionView.bounds.size.width - (cellsCount * self.cellSize) - ((cellsCount - 1) * self.cellSpacing)) * 0.5;
    horizontalInset = MAX(horizontalInset, 0.0);

    BOOL isLastSection = (section == self.numOfSections - 1);

    CGFloat verticalTopInset = self.cellSpacing;

    CGFloat verticalBottomInset = verticalTopInset;

    if (section == 0 && isLastSection == NO)
    {
        if (self.heightMiddleSpacing)
        {
            verticalBottomInset += self.heightMiddleSpacing;
        }
        verticalBottomInset /= 2;
    }

    if (section > 0)
    {
        if (self.heightMiddleSpacing)
        {
            verticalTopInset += self.heightMiddleSpacing;
        }
        verticalTopInset /= 2;

        if (isLastSection == NO)
        {
            if (self.heightMiddleSpacing)
            {
                verticalBottomInset += self.heightMiddleSpacing;
            }
            verticalBottomInset /= 2;
        }
    }

    return UIEdgeInsetsMake(verticalTopInset, horizontalInset, verticalBottomInset, horizontalInset);
}

@end

And for it to work, all we need to do in the parent view is:

self.collectionView.delegateCenteringCollection = self;
self.collectionView.dataSourceCount = 5; // Or whatever number we want!

: collectionView " ", " " " " 0 1 ( " ": collectionView). , , "autoResize" "true", , ( ) . autoResize true, , collectionView, . collectionView , , 3- , 3.

!

+2

1: ( )

2 .

  • 1 3

  • 2 2

collection​View(_:​layout:​inset​For​Section​At:​) , . ( 2)

, "", . .

. . . .

2: ( , )

2, UICollectionViewCell contentView.

2 .

+5

. . Collection.

() :

+4
source

You can get this type of format in a UICollectionView by changing the number of sections from 1 to 2.

And then you can define your custom UICollectionViewFlowLayout respectively for different sections.

+1
source

Source: https://habr.com/ru/post/1672680/


All Articles