解读自定义UICollectionViewLayout--感动了我自己

本文深入探讨了如何自定义UICollectionView的布局,通过创建一个继承自UICollectionViewLayout的类,实现自定义布局逻辑,包括计算可滚动区域大小、cell、header、footer及装饰的布局信息。提供了从初始化到具体实现的详细步骤,并附带了一个简单的demo,帮助开发者掌握自定义布局技巧。

  本文主要介绍了如何自定义UICollectionViewLayout。

    一、简介UICollectionView的布局

  首先我们回顾一下跟UICollectionView的几个类,看图说话。

  简单解释上图,UICollectionView有个属性是collectionViewLayout,这个属性就是用来描述collectionView中的控件布局的,一般在初始化UICollectionView的时候,我们会传入一个UICollectionViewLayout对象来描述布局信息。常见的UICollectionViewFlowLayout (流水布局)太简单,本文就不赘述了,只需要知道它是官方提供的一个布局特例(好多布局信息都为我们写好了),继承自UICollectionViewLayout。其实,真正描述UICollectionView中cell等位置的是进一步封装在UICollectionViewLayout中的UICollectionViewLayoutAttributes(布局属性),attributes对象中包含了索要描述的控件的位置,大小,透明度,层级等信息。UICollectionViewLayout会把描述各个控件的attributes对象,封装到一个数组里面去,统一返回给collectionView,告诉collectionView就按照这个“说明书”来布局。

  知道了这个关系,重点就是 自定义一个 UICollectionViewLayout 类,然后赋值给collectionView.collectionViewLayout。

  补充下,collectionView的组成元素:

  1、cell

  2、header 、footer(统称SupplementaryView)

  3、decorationView ,装饰,空间层次关系:在collectionView的背景之上,cell之下的一个视图,ibooks里面的那个书架就是它

   

  二、自定义UICollectionViewLayout

  首先建一个类继承自 UICollectionViewLayout,对了,每个Layout都有一个属性是collectionView,对应自己所属的collectionView。

 

@interface LTCollectionViewLayout : UICollectionViewLayout

   其次,在.m文件中,实现以下方法(按系统调用它们的顺序写的,当然四五六方法是我们主动调用的)

  (一)-(void)prepareLayout     

      在这个方法中一般进行一些数据的初始化,为下面的方法准备一些数据。记得一定要调用[super prepareLayout];

  (二)-(CGSize)collectionViewContentSize

      返回collectionView的可以滚动的区域大小。非必须重写方法,但笔者建议重写。

  (三)-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect

      这个方法是最主要的,用来把所有的布局信息打包到一个数组里面,返回,告诉系统怎么布局。在该方法中我们主动调用下面三个方法获取想要的布局信息。

  (四)-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath

      返回indexPath处cell的布局信息,也就是说,cell的布局代码在这个方法里面写。

    (五)如果有各组有header或footer,or both,就实现

        -(UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath

      返回indexPath处的header或footer的布局信息

  (六)如果某一section有装饰,就实现

     -(UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath

      返回indexPath处的装饰的布局信息

其实,到这里,我们基本上已经知道怎么自定义layout了,下面给出一个非常简单的demo。

  先上图,git上的demo

                                 

  

.h文件

@interface LTCollectionViewLayout : UICollectionViewLayout

@end

 .m文件

#define decorationView @"decorationView"

#import "LTCollectionViewLayout.h"
#import "LTDecotationView.h"

@interface LTCollectionViewLayout()
@property(nonatomic,readonly)CGFloat cellCount;
@property(nonatomic,readonly)CGPoint center;
@property(nonatomic,readonly)CGFloat radius;

@end

@implementation LTCollectionViewLayout

-(void)prepareLayout
{
    [super prepareLayout];
    NSLog(@"prepareLayout");
    
    CGSize size=self.collectionView.frame.size;
    _cellCount=[[self collectionView] numberOfItemsInSection:0];
//    圆心
    _center=CGPointMake(size.height/3.5, size.width/1.5);
//    半径
    _radius=MIN(size.height, size.width)/2.5;
    
//    如果用到了装修,那么必须注册装饰,官方提供了以下两个方法
    
//    - (void)registerClass:(Class)viewClass forDecorationViewOfKind:(NSString *)elementKind;
//    - (void)registerNib:(UINib *)nib forDecorationViewOfKind:(NSString *)elementKind;
    
//    注册装饰
    [self registerClass:[LTDecotationView class] forDecorationViewOfKind:decorationView];
    
}

/**
 *  此方法返回collectionView可以滚动的区域大小
 *
 */
-(CGSize)collectionViewContentSize
{
     NSLog(@"collectionViewContentSize");
    return CGSizeMake(375, 2000);
   
}
/**
 *  描述每个cell的布局信息,包括大小,位置等,布局信息封装到UICollectionViewLayoutAttributes对象中
 */
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"layoutAttributesForItemAtIndexPath");
    UICollectionViewLayoutAttributes *attributes=[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
//    每个cell的大小
    attributes.size=CGSizeMake(34.0 , 34.0);
//    使用三角函数计算每个cell的中点位置
    attributes.center=CGPointMake(_center.x + _radius*cosf(2*M_PI*indexPath.item/_cellCount), _center.y+_radius*sinf(2*M_PI*indexPath.item/_cellCount));
    return attributes;
}

/**
 *  该方法返回值也是UICollectionViewLayoutAttributes对象,attributes对象中已经包含了SupplementaryView的布局信息,即header或footer,区分的方法是ofKind:后面的字符串
 */
-(UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
{
//    注意:初始化方法和cell、DecorationView不同
    UICollectionViewLayoutAttributes *attributes=[UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:indexPath];
    
    return attributes;
}

/**
    使用了装饰,实现这个方法来描述DecorationView的布局
 *  该方法返回值也是UICollectionViewLayoutAttributes对象,attributes对象中已经包含了装饰,使用它需要在自定义的layout的prepareLayout方法中注册,因为collectionView没有提供它的注册方法
 *
 */
-(UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
{
//    注意:初始化方法和cell、SupplementaryView不同
    UICollectionViewLayoutAttributes *attributes=[UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:@"decorationView" withIndexPath:indexPath];
    
//    放在圆心处
        attributes.center=self.center;
//    尺寸
        attributes.size=CGSizeMake(150,30);

    return attributes;
    
}

/**
 *  对一次显示某个区域的时候自动调用,对显示过的区域不会再调用该方法
 */
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    
     NSLog(@"layoutAttributesForElementsInRect,%@",NSStringFromCGRect(rect));
    NSMutableArray *attributes=[NSMutableArray array];
    
//    1、添加cell的布局描述
        for (int i =0; i<self.cellCount; i++) {
            NSIndexPath *indexPath=[NSIndexPath indexPathForItem:i inSection:0];
//            调用对应的方法获得indexPath处的cell布局信息,并添加到数组中
            [attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
        }
    
//    2、,如果有,添加header或footer的布局描述
     NSIndexPath *indexPath=[NSIndexPath indexPathForItem:0 inSection:0];
//      调用对应的方法获得indexPath处的header或footer布局信息,并添加到数组中
//    [attributes addObject:[self layoutAttributesForSupplementaryViewOfKind:@"header or footer" atIndexPath:indexPath]];
    
//    3、如果有,添加decorationView的布局描述
    
//       调用对应的方法获取indexPath处decorationView的布局信息
    [attributes addObject:[self layoutAttributesForDecorationViewOfKind:@"decorationView" atIndexPath:indexPath]];
    
//    4、返回装有各种布局信息的数组给collectionView
    return attributes;
  

}
@end

 

自定义的decoration,

.h

@interface LTDecotationView : UICollectionReusableView

@end

 .m

#import "LTDecotationView.h"

@implementation LTDecotationView

-(instancetype)initWithFrame:(CGRect)frame
{
    if (self=[super initWithFrame:frame]) {
        
        UILabel *label=[[UILabel alloc]initWithFrame:self.bounds];
        label.text=@"DecotationView";
        label.backgroundColor=[UIColor whiteColor];
        [self addSubview:label];
    }
    
    return self;
}

@end

 

控制器.m文件

#define  collectionCell @"collectionCell"
#import "ViewController.h"
#import "LTCollectionViewLayout.h"

@interface ViewController ()<UICollectionViewDataSource,UICollectionViewDelegate>
@property(nonatomic,strong)UICollectionView *collectionView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
//    1、初始化collectionView
    LTCollectionViewLayout *myLayout=[[LTCollectionViewLayout alloc]init];
    
    UICollectionView * myCollectionView=[[UICollectionView alloc]initWithFrame:CGRectMake(0, 0, 375, 667) collectionViewLayout:myLayout];
    
    myCollectionView.backgroundColor=[UIColor purpleColor];
    
    self.collectionView=myCollectionView;
    [self.view addSubview:myCollectionView];
//    2、设置代理
    self.collectionView.delegate=self;
    self.collectionView.dataSource=self;
    
//    3、注册cell
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:collectionCell];
    

}

-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return 1;
}


-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return 20;
}

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:collectionCell forIndexPath:indexPath];
    cell.backgroundColor=[UIColor orangeColor];

    return cell;
    
}

 

转载于:https://www.cnblogs.com/panyuluoye/p/4923205.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值