iOS UICollectionView特殊单元格顺序

时间:2017-03-19 17:43:31

标签: ios uicollectionview

我有一个包含5个元素的简单集合视图。 单元格的默认顺序如下所示: enter image description here

我想知道是否可以更改单元格的填充\顺序以获得如下结果:

enter image description here

4 个答案:

答案 0 :(得分:5)

选项1 :(清洁选项)

您应该 2个部分

  • 第1节有3个单元格

  • 第2节有2个单元格

然后您可以 adjust the inset collection​View(_:​layout:​inset​For​Section​At:​)一起使用您要调整的部分。 (在这种情况下,第2节)

  

如果未实现此方法,则流布局将使用值   其部分Inset属性用于设置边距。您的   此方法的实现可以返回一组固定的边距大小   或者为每个部分返回不同的边距大小。

     

部分插入是   边距仅适用于该部分中的项目。他们代表了   标题视图与第一行项目之间的距离和   在最后一行项目和页脚视图之间。他们还表示   它们在一行物品的两侧间隔开。他们不   影响页眉或页脚本身的大小。

选项2 :(意大利面条选项,但很高兴知道)

为第2部分项创建自定义子类,您可以在其中自定义UICollectionViewCell contentView子视图的实际内容的插入内容。

然后在第2部分中,返回您的自定义单元格。

答案 1 :(得分:4)

为此,您可能需要编写自定义布局。有关详细信息,请查看Collection View Programming Guide

默认(流)布局将始终以此模式放置单元格:

答案 2 :(得分:2)

您可以通过将部分数量从1更改为2来在UICollectionView中实现此类型的格式。

然后,您可以相应地为不同的部分定义自定义UICollectionViewFlowLayout。

答案 3 :(得分:2)

解决方案:我创建了一个名为“CenteringCollectionView”的UICollectionView子类,并对我制作的部分进行了一些计算!

.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

.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

为了使其有效,我们在父视图中需要做的就是:

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

在故事板中:我们需要创建此类的collectionView并设置“行中的元素”值,同时将“单元格标识符”和“单元格相对大小”设置为0到1之间(“单元格相对大小”值:将根据collectionView width&amp; height)计算单元格大小和填充。 最后 - 如果您希望集合视图根据行数自动调整其自身的高度约束(如果存在),则将“autoResize”设置为“true”。如果我们将“autoResize”设置为true,我们设置为collectionView的高度约束将确定单个行的高度。如果我们的collectionView应该增长到例如3行,它将使我们的collectionview高度约束加倍3。

它就像一个魅力!