简体   繁体   English

iOS UICollectionView特殊单元格顺序

[英]iOS UICollectionView special cell order

I have a simple collection view with 5 elements. 我有一个简单的集合视图,包含5个元素。 The default order of the cells looks like this: 单元格的默认顺序如下所示: 在此输入图像描述

I wanna know if it's possible to change the padding\\order of the cells to get result like this: 我想知道是否可以更改单元格的填充\\顺序以获得如下结果:

在此输入图像描述

Option 1: (Clean option) 选项1 :(清洁选项)

You should have 2 sections . 你应该有2个部分

  • Section 1 with 3 cells 第1节有3个细胞

  • Section 2 with 2 cells 第2节有2个细胞

You can then adjust the inset with collection​View(_:​layout:​inset​For​Section​At:​) for the section you want to adjust. 然后,您可以使用要调整的部分的 collection​View(_:​layout:​inset​For​Section​At:​) 调整插入 (in this case, section 2) (在这种情况下,第2节)

If you do not implement this method, the flow layout uses the value in its section​Inset property to set the margins instead. 如果未实现此方法,则流布局将使用其“插入”属性部分中的值来设置边距。 Your implementation of this method can return a fixed set of margin sizes or return different margin sizes for each section. 您对此方法的实现可以返回一组固定的边距大小,或为每个部分返回不同的边距大小。

Section insets are margins applied only to the items in the section. 节插入是仅应用于节中的项的边距。 They represent the distance between the header view and the first line of items and between the last line of items and the footer view. 它们表示标题视图与第一行项目之间以及最后一行项目与页脚视图之间的距离。 They also indicate they spacing on either side of a single line of items. 它们还表明它们在单行项目的两侧间隔。 They do not affect the size of the headers or footers themselves. 它们不会影响页眉或页脚本身的大小。

Option 2: (Spaghetti option, but good to know) 选项2 :(意大利面条选项,但很高兴知道)

Create a custom subclass for section 2 items, where you can customize the inset of the actual content for the UICollectionViewCell contentView subviews. 为第2部分项创建自定义子类,您可以在其中自定义UICollectionViewCell contentView子视图的实际内容的插入。

Then in section 2, return your customized cell. 然后在第2部分中,返回您的自定义单元格。

To do this, you may need to write a custom layout. 为此,您可能需要编写自定义布局。 Check out the Collection View Programming Guide for more information. 有关详细信息,请查看“ 集合视图编程指南”

The default (flow) layout will always lay the cells out in this pattern: 默认(流)布局将始终以此模式放置单元格:

You can achieve this type of format in UICollectionView by changing the number of sections from 1 to 2. 您可以通过将节数从1更改为2来在UICollectionView中实现此类格式。

And then you can define your custom UICollectionViewFlowLayout accordingly for different sections. 然后,您可以相应地为不同的部分定义自定义UICollectionViewFlowLayout。

SOLUTION: I've created a subclass of UICollectionView named "CenteringCollectionView" and with a few calculations of the sections I made it! 解决方案:我已经创建了一个名为“CenteringCollectionView”的UICollectionView子类,并对我创建的部分进行了一些计算!

The .h file: .h文件:

#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

The .m file: .m文件:

#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 to make it work, all we need to do in the parent view is: 为了使它工作,我们在父视图中需要做的就是:

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

In the storyboard: we need to create collectionView of this class and set the "elements in row" value, also set the "Cell Identifier" and the "Cell relative size" between 0 to 1 (the "Cell relative size" value: will calculate the cell size & paddings according to the collectionView width & height). 在故事板中:我们需要创建此类的collectionView并设置“行中的元素”值,同时将“单元格标识符”和“单元格相对大小”设置为0到1(“单元格相对大小”值:将根据collectionView宽度和高度计算单元格大小和填充。 And at last - set "autoResize" to "true" if you want that the collection view will resize its own height constraint(if exist) automatically according to the number of rows. 最后 - 如果您希望集合视图根据行数自动调整其自身的高度约束(如果存在),则将“autoResize”设置为“true”。 If we set "autoResize" to true, the height constraint that we set to the collectionView will determine the height of a single row. 如果我们将“autoResize”设置为true,我们设置为collectionView的高度约束将确定单个行的高度。 If our collectionView should grow for example to 3 rows, it will multiple our collectionview height constraint by 3. 如果我们的collectionView应该增长到例如3行,它将使我们的collectionview高度约束乘以3。

And it works like a charm! 它就像一个魅力!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM