iOS - 版面实现记录四

本文介绍了在iOS中如何实现自定义UICollectionView布局,通过继承UICollectionViewLayout类并重载相关方法,如prepareLayout,以提供布局信息。内容涉及到设置contentSize,返回不同类型的UICollectionViewLayoutAttributes,以及对第三方库RAMCollectionViewFlemishBondLayout中计算Cell原点Y值的代码注解分析。
摘要由CSDN通过智能技术生成

版面四

同样使用 UICollectionView 来实现,自定义一个 UICollectionViewLayout 即可。UICollectionView 可以看作是具备复用控件的 ScrollView。自定义则是通过某些算法得到每一个控件在 Content 上的位置,并设置它具备的形态。该 UICollectionViewLayout 中获取并返回一个自定义的 UICollectionViewLayoutAttributes。

当UICollectionView在获取布局时将针对每一个indexPath的部件(包括cell,追加视图和装饰视图),向其上的UICollectionViewLayout实例询问该部件的布局信息(在这个层面上说的话,实现一个UICollectionViewLayout的时候,其实很像是zap一个delegate,之后的例子中会很明显地看出),这个布局信息,就以UICollectionViewLayoutAttributes的实例的方式给出。

简单地说就是 CollectionView 找 UICollectionViewLayout 要布局信息。UICollectionViewLayout 就给出 UICollectionViewLayoutAttributes 的实例。UICollectionViewLayout 在这过程中像提款机,UICollectionViewLayoutAttributes就像提款机中的钱。UICollectionView 需要钱,按提款机就行。

继承UICollectionViewLayout类,然后重载下列方法:
  • - (void)prepareLayout; `首先,-(void)prepareLayout将被调用,默认下该方法什么没做,但是在自己的子类实现中,一般在该方法中设定一些必要的layout的结构和初始需要的参数等。`
  • - (CGSize)collectionViewContentSize; 返回 UICollectionView 的 contentSize
    - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect; 返回包括 cell 的 Attribute,SupplementaryView 和 DecorationView 的 Attribute 的数组。每个 Attribute 通过 representedElementCategory 属性来在数组中区分该 Attribute 是属于Cell 抑或是 supplementary view 或 decoration view。
    可以重载三种对应方法返回 Attribute
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;
- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;
- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString*)elementKind atIndexPath:(NSIndexPath *)indexPath;

通过对应方法返回对应 Attribute

+ (instancetype)layoutAttributesForCellWithIndexPath:(NSIndexPath *)indexPath;
+ (instancetype)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind withIndexPath:(NSIndexPath *)indexPath;
(instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath*)indexPath;


下面是一种实现

#pragma mark - 
//
//  JDCollectionViewLayout.m
//  JasonCaoDemoProject
//
//  Created by 曹 景成 on 15/6/4.
//  Copyright (c) 2015年 JasonCao. All rights reserved.
//
@interface JDCollectionViewLayout : UICollectionViewLayout

@property (nonatomic, assign) NSInteger cellCount;
@property (nonatomic) CGSize headerReferenceSize;

@end

@implementation JDCollectionViewLayout
- (void)prepareLayout {
    [super prepareLayout];
    _cellCount = [[self collectionView] numberOfItemsInSection:0];
}

- (CGSize)collectionViewContentSize {
    NSInteger height = (_cellCount/7 +1)*262 + self.headerReferenceSize.height;
    return CGSizeMake(CGRectGetWidth(self.collectionView.frame), height);
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path
{
    UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];

    //每一小组起点(x, y)
    NSInteger y = (path.row/7)*262 + self.headerReferenceSize.height;
    NSInteger x = 8;

    //每个cell在每个小组当中的位置
    NSInteger indexForSix = (path.row+1)%7;

    if(indexForSix == 0 || indexForSix == 6){
        //小组当中下方两个中等cell
        y += 254*0.4+14;
        attributes.size = CGSizeMake((IOS_SCREEN_WIDTH-24)/2, 254*0.6);
        if(indexForSix == 0) x = IOS_SCREEN_WIDTH/2+4;
    }else if (indexForSix == 1){
        //每一小组第一个cell
        y += 8;   //距离原点18点
        attributes.size = CGSizeMake((IOS_SCREEN_WIDTH-24)/2, 254*0.4);
    }else {
        attributes.size = CGSizeMake((((IOS_SCREEN_WIDTH-24)/2)-8)/2, (254*0.4-8)/2);
        if(indexForSix == 2){
            y += 8;
            x = IOS_SCREEN_WIDTH/2+4;
        }
        if(indexForSix == 3){
            y += 8;
            x = IOS_SCREEN_WIDTH/2+(((IOS_SCREEN_WIDTH-24)/2)-8)/2+12;
        }
        if(indexForSix == 4){
            y += (254*0.4-8)/2+16;
            x = IOS_SCREEN_WIDTH/2+4;
        }

        if(indexForSix == 5){
            y += (254*0.4-8)/2+16;
            x = IOS_SCREEN_WIDTH/2+(((IOS_SCREEN_WIDTH-24)/2)-8)/2+12;
        }
    }

    attributes.center = CGPointMake(x+attributes.size.width/2, y+attributes.size.height/2);
    return attributes;
}

//使用 Reveal 查看 headerView 被加了8个,只有一个 View 包含 SaKuraScrollView
- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *headerAttributes = [UICollectionViewLayoutAttributes
                                                          layoutAttributesForSupplementaryViewOfKind:elementKind
                                                          withIndexPath:indexPath];
    headerAttributes.size = self.headerReferenceSize;
    headerAttributes.center = CGPointMake(IOS_SCREEN_WIDTH/2, headerAttributes.size.height/2);
    NSLog(@"header---%lu",(unsigned long)headerAttributes.representedElementCategory);
    return headerAttributes;
}

-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray* attributes = [NSMutableArray array];
    for (NSInteger i=0 ; i < self.cellCount; i++) {
        NSIndexPath* indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        [attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
    }
    NSIndexPath* indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
    [attributes addObject:[self layoutAttributesForSupplementaryViewOfKind:@"header" atIndexPath:indexPath]];
    return attributes;
}

@end


使用如下

- (void)initContentCollectionView {
    JDCollectionViewLayout *layout = [[JDCollectionViewLayout alloc] init];
    layout.headerReferenceSize = CGSizeMake(IOS_SCREEN_WIDTH, 200);

   self.contentCollectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0 ,64.f , IOS_SCREEN_WIDTH, IOS_SCREEN_CONTENT_HEIGHT - BOTTOM_MENU_BAR_HEIGHT) collectionViewLayout:layout];
    self.contentCollectionView.backgroundColor = LIGHT_GRAY;
    self.contentCollectionView.delegate = self;
    self.contentCollectionView.dataSource = self;
    self.contentCollectionView.showsVerticalScrollIndicator = NO;
    self.contentCollectionView.alwaysBounceVertical = YES;
    //collectionView(上、左、下、右)
    self.contentCollectionView.contentInset = COLLECTIONCONTENTINSET;
    [self.contentCollectionView registerClass:[ViewHomeMenuCollectionCell class] forCellWithReuseIdentifier:@"cell"];
    [self.contentCollectionView registerClass:[ViewHomeMenuCollectionCellMedium class] forCellWithReuseIdentifier:@"mediumCell"];
    [self.contentCollectionView registerClass:[ViewHomeMenuCollectionCellSmall class] forCellWithReuseIdentifier:@"smallCell"];
    [self.contentCollectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"headAdView"];
    [self addSubview:self.contentCollectionView];
}

另外还有类似的第三方库
RAMCollectionViewFlemishBondLayout

对 RAMCollectionViewFlemishBondLayout 库其中一段计算 Cell 的原点Y值代码注释记录

//获取每一个cell的原点Y值
- (CGFloat)getYAtIndexPath:(NSIndexPath *)indexPath
{
    NSInteger currentGroup = [self currentGroupAtIndexPath:indexPath];
    CGFloat yValue = 0.0f;
    NSIndexPath *indexPathFirstElementCurrentSection = [NSIndexPath indexPathForRow:0 inSection:indexPath.section];
    if ([self isHighLightedElementAtIndexPath:indexPath]) {
        //判断是否是 highLight ,计算得到 y 值
        yValue = (currentGroup - 1) * self.highlightedCellHeight + [self heightHeaderAtIndexPath:indexPathFirstElementCurrentSection];
    } else {
        //这里需要加判断是否为下方两个中等cell?
        //不是 highLight
        NSInteger position;
        //numberOfElements 为每一个小组的数量
        //这里的思路是仅仅用算法计算每一个Cell的frame,按规律放到CollecitonView的Content上
        //使用CollectionView而非ScrollView为了更好地实现复用
        //
        if (indexPath.row <= self.numberOfElements) {
            //这里得到第一小组的小cell的位置position
            //从 position = (indexPath.row - 1); 修改为
            position = (indexPath.row - 1)%2;
            //还需要算出positionX的位置
        } else {
            //第二小组及以后
            NSInteger maxElement = self.numberOfElements * currentGroup;
            //(maxElement - self.numberOfElements)可以算得每一个小组highLight的下标
            position = (indexPath.row - 1) - (maxElement - self.numberOfElements);
        }
        if (position == 6 && position == 7) {
            //            yValue = ((currentGroup - 1) * self.highlightedCellHeight) +
        } else {
            yValue = ((currentGroup - 1) * self.highlightedCellHeight) + (self.cellHeight * position) + [self heightHeaderAtIndexPath:indexPathFirstElementCurrentSection];
        }
    }
    if (indexPath.section > 0) {
        yValue += (self.highlightedCellHeight * indexPath.section * [self totalGroupsAtIndexPath:indexPath]) + [self headerAndFooterHeightsPreviouslyAtIndexPath:indexPath];
    }
    return yValue;
}

参考:
UICollectionViewLayoutAttributes Class Reference
UICollectionView之自定义布局
iOS6新特征:UICollectionView介绍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值