【iOS】—— UICollectionView基础

【iOS】—— UICollectionView基础,第1张

一、UICollectionView基础: 1.简介:

UICollectionView是iOS6之后引入的一个新的UI控件,在目前的iOS开发中,使用非常广泛。它和UITableView都继承自UIScrollView,但是它比UITableView更加强大,可以根据需要自定义各种各样复杂的布局。
它和UITableView十分相似,并且很多代理方法都十分类,具体的可以看看苹果官方的文档:UICollectionView - Apple Developer

2.特点: (1)系统自带的流水布局支持水平和垂直两种方式的布局; (2)通过layout配置方式进行布局; (3)collectionview中item的大小和位置可以自定义; (4)可以自定义一套layout的布局方案; 3.常用属性和方法: UICollectionViewFlowLayout相关属性方法 (1)初始化cell样式
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
(2)布局类的方向
layout.scrollDirection = UICollectionViewScrollDirectionVertical;

UICollectionViewScrollDirectionVertical 竖向排布
UICollectionViewScrollDirectionHorizontal 横向排布

(3)设置item的大小
layout.itemSize = CGSizeMake(120, 100);
(4)设置item的上下间距
layout.minimumLineSpacing = 100;
(5)设置item的左右间距
layout.minimumInteritemSpacing = 10;
(6)设置item的内间距
layout.sectionInset = UIEdgeInsetsMake(5, 5, 5, 5);
(7)设置item的头部高度
layout.headerReferenceSize = CGSizeMake(self.view.bounds.size.width, 50);
(8)设置item的低部高度
layout.footerReferenceSize = CGSizeMake(self.view.bounds.size.width, 50);
(9)设置分区的头视图和尾视图是否始终固定在屏幕上边和下边

@property (nonatomic) BOOL sectionHeadersPinToVisibleBounds NS_AVAILABLE_IOS(9_0);
@property (nonatomic) BOOL sectionFootersPinToVisibleBounds NS_AVAILABLE_IOS(9_0);

UICollectionView相关属性方法 (1)UICollectionView实例化对象和大小
self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];

最后的collectionViewLayout:layout是该UICollectionView遵守的布局类。

(2)刷新对应的列
[self.collectionView performBatchUpdates:^{
    [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:nil];

UICollectionView示例对象的用法和UITableView相似,例如代理、分页效果、代理方法跟数据源方法的实现等等。唯一要注意的就是UICollectionView必须使用注册的方法,注册类似于UITableView的注册方法,所以获取item时就要使用:- (__kindof UICollectionViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath;方法。

4.为什么UICollectionView必须使用注册的方法?

UICollectionView的dataSource中的item的注册,itme的注册和tableview中cell的创建是不同的,cell的创建是在cell中,而itme的注册是在viewDidLoad中,因为系统中加载好了tableview后再去加载cell,而如果加载界面时没有item会报错。

5.协议和代理方法: (1)遵守的协议:
<UICollectionViewDelegate, UICollectionViewDataSource>
(2)代理方法: UICollectionViewDelegate协议

该协议用来设置和处理collectionView的功能和一些逻辑,所有的方法都是可选实现

是否允许某个item的高亮,返回NO,则不能进入高亮状态
- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath;当item高亮时触发的方法
- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath;结束高亮状态时触发的方法
- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath;是否可以选中某个item,返回NO,则不能选中
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath;是否可以取消选中某个item
- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath;已经选中某个item时触发的方法
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;取消选中某个item时触发的方法
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath;将要加载某个item时调用的方法
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0);将要加载头尾视图时调用的方法
- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0);已经展示某个item时触发的方法
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath;已经展示某个头尾视图时触发的方法
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;UICollectionView进行重新布局时调用的方法
- (nonnull UICollectionViewTransitionLayout *)collectionView:(UICollectionView *)collectionView transitionLayoutForOldLayout:(UICollectionViewLayout *)fromLayout newLayout:(UICollectionViewLayout *)toLayout; UICollectionViewDataSource协议

必须实现的方法:

设置每个分区的item数
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section;设置返回每个item的属性
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;
可选实现的方法:设置分区数,虽然这个方法是可选的,一般我们都会去实现,不去设置它的话,它的默认值为1
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView;对头视图或者尾视图进行设置
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;设置某个item是否可以被移动,返回NO则不能移动
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0);移动item的时候,会调用这个方法
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath; 二、UICollectionView实现一个简单的九宫格类布局:

ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController<UICollectionViewDelegate, UICollectionViewDataSource>

@property (nonatomic, strong) UICollectionViewFlowLayout *layout;
@property (nonatomic, strong) UICollectionView *collectionView;

@end

ViewController.m

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor grayColor];
    
    //layout布局类
    self.layout = [[UICollectionViewFlowLayout alloc] init];
    //布局方向为垂直流布局
    self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
    
    //设置每个item的大小
    self.layout.itemSize = CGSizeMake(120, 100);
    
    //UICollectionView实例化对象和大小
    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:self.layout];
    
    //代理设置
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    
    //注册item类型,这里使用系统类型
    //必须注册!!!!,注册类似于tableView,但又不同于tableView,因为tableView有不注册的方法,临时创建
    //iOS6之后的新类,统一从cell复用池获取cell,没有提供返回nil的方式
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
    [self.view addSubview:self.collectionView];
}

//返回分区个数
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}

//返回每个分区的item个数
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 30;
}


//返回每个item
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    //只能使用这个方法获取,其他方法都会崩溃
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
    
    cell.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 255.0 green:arc4random() % 255 / 255.0 blue:arc4random() % 250 / 250.0 alpha:1];
    
    return cell;
}

@end


当然你如果想在UICollectionViewCell中添加控件之类的东西,它的 *** 作和UITableView是相似的,不过它继承的是UICollectionViewCell类,并且在重写cell中使用的是- (instancetype)initWithFrame:(CGRect)frame方法。
下面我就重写了UICollectionViewCell类,并且在其中添加了一个UIImageView效果如下:
ImageCollectionViewCell.h

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface ImageCollectionViewCell : UICollectionViewCell

@property (nonatomic, strong) UIImageView *showImageView;

@end

NS_ASSUME_NONNULL_END

ImageCollectionViewCell.m

#import "ImageCollectionViewCell.h"

@implementation ImageCollectionViewCell

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    
    if (self) {
        self.showImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 120, 100)];
        [self.contentView addSubview:self.showImageView];
    }
    
    
    return self;
}

@end

注意,因为这里使用的是- (instancetype)initWithFrame:(CGRect)frame方法,重写的类没有接收到你的reuseIdentifier所以这里没有办法对重写进行身份判断然后根据判断来创建控件。
目前还未找到解决方法。
ViewController.m

#import "ViewController.h"
#import "ImageCollectionViewCell.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor grayColor];
    
    //layout布局类
    self.layout = [[UICollectionViewFlowLayout alloc] init];
    //布局方向为垂直流布局
    self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
    
    //设置每个item的大小
    self.layout.itemSize = CGSizeMake(120, 100);
    
    //UICollectionView实例化对象和大小
    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:self.layout];
    
    //代理设置
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    
    //注册item类型,这里使用系统类型
    //必须注册!!!!,注册类似于tableView,但又不同于tableView,因为tableView有不注册的方法,临时创建
    //iOS6之后的新类,统一从cell复用池获取cell,没有提供返回nil的方式
    [self.collectionView registerClass:[ImageCollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
    [self.view addSubview:self.collectionView];
}

//返回分区个数
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}

//返回每个分区的item个数
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 30;
}


//返回每个item
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    //只能使用这个方法获取,其他方法都会崩溃
    ImageCollectionViewCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
    NSString *tempString = [[NSString alloc] initWithFormat:@"1-%ld.jpeg", indexPath.row % 4 + 1];
    cell.showImageView.image = [UIImage imageNamed:tempString];
    
    return cell;
}

@end

三、使用FlowLayout进行灵活布局:

UICollectionViewLayout是UICollectionView实现各式复杂布局的核心,需要我们去自定义实现。

1.UICollectionViewFlowLayout的动态设置方法:

上面我们说了一些UICollectionViewFlowLayout静态时的设置,如果我们需要动态的设置这些属性,及每个item的大小会有差异,我们可以通过代理来实现,UICollectionViewDelegateFlowLayout是UICollectionViewDelegate的子协议,其中常用方法如下,我们只需要实现我们需要的即可:

动态设置每个item的尺寸大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;动态设置每个分区的EdgeInsets
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;动态设置每行的间距大小
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;动态设置每列的间距大小
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;动态设置某个分区头视图大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section;动态设置某个分区尾视图大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section; 2.UICollectionView升级版:

上边我们写了简单的九宫格,但是总体来说没什么特殊的感觉,这里我们刚说了动态设置UICollectionViewFlowLayout我们来试试,下面我在上述代码的基础上添加了:

//动态设置每个item的尺寸大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row % 2) {
        return CGSizeMake(100, 100);
    } else {
        return CGSizeMake(150, 150);
    }
}

因为我之前在重写UICollectionView的时候固定了其中的showImageView所以,仅仅添加这个代码是不够的,还的重新更新showImageView,这里我在返回item的时候进行了重新更新:

//返回每个item
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    //只能使用这个方法获取,其他方法都会崩溃
    ImageCollectionViewCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
    NSString *tempString = [[NSString alloc] initWithFormat:@"1-%ld.jpeg", indexPath.row % 4 + 1];
    cell.showImageView.image = [UIImage imageNamed:tempString];
    if (indexPath.row % 2) {
        cell.showImageView.frame = CGRectMake(0, 0, 100, 100);
    } else {
        cell.showImageView.frame = CGRectMake(0, 0, 150, 150);
    }
    
    return cell;
}

最终得到的效果如下:

如果看着还是不太舒服的话,我们还可以使用上述说过的sectionInset属性设置分区的偏移量,这里就不再过多的进行演示了,自己可以根据自己想要的效果试试。

四、进行自定义瀑布流布局: 1.首先,我们新建一个文件继承于UICollectionViewFlowLayout:
@interface MyLayout : UICollectionViewFlowLayout

为了演示的方面,这里我不做更多的封装,添加一个属性,直接让外界将item个数传递进来,我们把重心方法重写布局的方法上:

@interface MyLayout : UICollectionViewFlowLayout

@property (nonatomic, assign) int itemCount;

@end

前面说过,UICollectionViewFlowLayout是一个专门用来管理collectionView布局的类,因此,collectionView在进行UI布局前,会通过这个类的对象获取相关的布局信息,FlowLayout类将这些布局信息全部存放在了一个数组中,数组中是UICollectionViewLayoutAttributes类,这个类是对item布局的具体设置,以后咱们再讨论这个类。总之,FlowLayout类将每个item的位置等布局信息放在一个数组中,在collectionView布局时,会调用FlowLayout类中layoutAttributesForElementsInRect:方法来获取这个布局配置数组。因此,我们需要重写这个方法,返回我们自定义的配置数组,另外,FlowLayout类在进行布局之前,会调用prepareLayout方法,所以我们可以重写这个方法,在里面对我们的自定义配置数据进行一些设置。

简单来说,自定义一个FlowLayout布局类就是两个步骤: 设计好我们的布局配置数据,prepareLayout方法中返回我们的配置数组,layoutAttributesForElementsInRect方法中 2.然后对该类进行重写:

MyLayout.h

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface MyLayout : UICollectionViewFlowLayout

@property (nonatomic, assign) int itemCount;

@end

NS_ASSUME_NONNULL_END

MyLayout.m

#import "MyLayout.h"

@implementation MyLayout {
    //自定义的配置数组
    NSMutableArray *_attributeArray;
}

//数组的相关设置在这个方法中
//布局前的准备 会调用这个方法
- (void)prepareLayout {
    _attributeArray = [[NSMutableArray alloc] init];
    [super prepareLayout];
    
    //设置为静态的2列
    //计算每一个item的宽度
    float itemWidth = ([UIScreen mainScreen].bounds.size.width - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing ) / 2;
    
    //定义数组保存每一列的高度
    //这个数组的主要作用是保存每一列的总高度,这个样在布局时,我们可以始终将下一个item放在最短的列下面
    CGFloat colHeight[2] = {self.sectionInset.top, self.sectionInset.bottom};
    
    //itemCount是外界传进来的item的个数 遍历来设置每一个item的布局
    for (int i = 0; i < _itemCount; i++) {
        //设置每一个item的位置等相关属性
        NSIndexPath *index = [NSIndexPath indexPathForItem:i inSection:0];
        //创建一个布局属性类, 通过indexPath来创建
        UICollectionViewLayoutAttributes *attris = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:index];
        //随意一个高度 在60-260之间
        CGFloat height = arc4random() % 200 + 60;
        
        //哪一行高度小 则放到哪一列下面
        //标记最短的列
        int flag = 0;
        if (colHeight[0] < colHeight[1]) {
            //将新的item高度加入到短的一列
            colHeight[0] = colHeight[0] + height + self.minimumLineSpacing;
            flag = 0;
        } else {
            colHeight[1] = colHeight[1] + height + self.minimumLineSpacing;
            flag = 1;
        }
        
        //设置item的位置
        attris.frame = CGRectMake(self.sectionInset.left + (self.minimumInteritemSpacing + itemWidth) * flag, colHeight[flag] - height - self.minimumLineSpacing, itemWidth, height);
        
        [_attributeArray addObject:attris];
    }
    
    //设置itemSize来确保滑动范围的正确 这里是通过将所有的item高度平均化,计算出来的 (以最高的列为标准)
    if (colHeight[0] > colHeight[1]) {
        self.itemSize = CGSizeMake(itemWidth, (colHeight[0] - self.sectionInset.top) * 2 / _itemCount - self.minimumLineSpacing);
    } else {
        self.itemSize = CGSizeMake(itemWidth, (colHeight[0] - self.sectionInset.top) * 2 / _itemCount - self.minimumLineSpacing);
    }
    
}

//返回布局数组
- (NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    return _attributeArray;
}

@end
3.在你要用到的地方添加该类,并定义属性:

ViewController.h

#import <UIKit/UIKit.h>
#import "MyLayout.h"

@interface ViewController : UIViewController< UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>

@property (nonatomic, strong) MyLayout *layout;
@property (nonatomic, strong) UICollectionView *collectionView;

@end

ViewController.m

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor grayColor];
    
    //layout布局类
    self.layout = [[MyLayout alloc] init];
    //布局方向为垂直流布局
    self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
    self.layout.itemCount = 30;
    
    //UICollectionView实例化对象和大小
    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:self.layout];
    
    //代理设置
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    
    //注册item类型,这里使用系统类型
    //必须注册!!!!,注册类似于tableView,但又不同于tableView,因为tableView有不注册的方法,临时创建
    //iOS6之后的新类,统一从cell复用池获取cell,没有提供返回nil的方式
    [self.collectionView registerClass:[ImageCollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
    [self.view addSubview:self.collectionView];
}

//返回分区个数
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}

//返回每个分区的item个数
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 30;
}


//返回每个item
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];

    cell.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 255.0 green:arc4random() % 255 / 255.0 blue:arc4random() % 250 / 250.0 alpha:1];
    return cell;
}

@end

效果如下:

4.UICollectionViewLayoutAttributes类中我们可以配置的属性:

通过上面的例子,我们可以了解,collectionView的item布局其实是LayoutAttributes类具体配置的,这个类可以配置的布局属性不止是frame这么简单,其中还有许多属性:

//配置item的布局位置
@property (nonatomic) CGRect frame;
//配置item的中心
@property (nonatomic) CGPoint center;
//配置item的尺寸
@property (nonatomic) CGSize size;
//配置item的3D效果
@property (nonatomic) CATransform3D transform3D;
//配置item的bounds
@property (nonatomic) CGRect bounds NS_AVAILABLE_IOS(7_0);
//配置item的旋转
@property (nonatomic) CGAffineTransform transform NS_AVAILABLE_IOS(7_0);
//配置item的alpha
@property (nonatomic) CGFloat alpha;
//配置item的z坐标
@property (nonatomic) NSInteger zIndex; // default is 0
//配置item的隐藏
@property (nonatomic, getter=isHidden) BOOL hidden; 
//item的indexpath
@property (nonatomic, strong) NSIndexPath *indexPath;
//获取item的类型
@property (nonatomic, readonly) UICollectionElementCategory representedElementCategory;
@property (nonatomic, readonly, nullable) NSString *representedElementKind; 
 
//一些创建方法
+ (instancetype)layoutAttributesForCellWithIndexPath:(NSIndexPath *)indexPath;
+ (instancetype)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind withIndexPath:(NSIndexPath *)indexPath;
+ (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath *)indexPath;
调用顺序: (1)-(void)prepareLayout 设置layout的结构和初始需要的参数等。(2)-(CGSize) collectionViewContentSize 确定collectionView的所有内容的尺寸。(3)-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect初始的layout的外观将由该方法返回的UICollectionViewLayoutAttributes来决定。(4)在需要更新layout时,需要给当前layout发送

1)-invalidateLayout, 该消息会立即返回,并且预约在下一个loop的时候刷新当前layout
2)-prepareLayout,
3)依次再调用-collectionViewContentSize和-layoutAttributesForElementsInRect来生成更新后的布局。

现在我们就可以通过这些给的方法创作自己想要的布局了,简直不要太cool。

五、圆环布局的实现:

和上边讲述的步骤一样,先创建一个继承UICollectionViewLayout的类,然后在该类中进行重写,最后在返回一个布局数组,再在你要使用的view中进行创建初始化,再让UICollectionView遵守这个布局类就大功告成了。
CircleLayout.h

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface CircleLayout : UICollectionViewFlowLayout

@property (nonatomic, assign) int itemCount;

@end

NS_ASSUME_NONNULL_END

CircleLayout.m

#import "CircleLayout.h"

@implementation CircleLayout {
    NSMutableArray *_attributeArray;
}

- (void)prepareLayout {
    
    [super prepareLayout];
    
    //获取item个数
    _itemCount = (int)[self.collectionView numberOfItemsInSection:0];
    _attributeArray = [[NSMutableArray alloc] init];
    
    //先设定大圆的半径 取长和宽最短的
    CGFloat radius = MIN(self.collectionView.frame.size.width, self.collectionView.frame.size.height) / 2;
    
    //计算圆心的位置
    CGPoint center = CGPointMake(self.collectionView.frame.size.width / 2, self.collectionView.frame.size.height / 2);
    
    //设置每个item的大小为50 * 50,半径为25
    for (int i = 0; i < _itemCount; i++) {
        UICollectionViewLayoutAttributes *attris = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
        
        //设置item大小
        attris.size = CGSizeMake(50, 50);
        
        //计算每个item的圆心位置
        float x = center.x + cosf(2 * M_PI / _itemCount * i) * (radius - 25);
        float y = center.y + sinf(2 * M_PI / _itemCount * i) * (radius - 25);
    
        attris.center = CGPointMake(x, y);
        [_attributeArray addObject:attris];
    }
}

//设置内容区域大小
- (CGSize)collectionViewContentSize {
    return self.collectionView.frame.size;
}

//返回设置数组
- (NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    return _attributeArray;
}

@end

ViewController.h

#import <UIKit/UIKit.h>
#import "CircleLayout.h"

@interface ViewController : UIViewController< UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>

@property (nonatomic, strong) CircleLayout *layout;
@property (nonatomic, strong) UICollectionView *collectionView;

@end

ViewController.m

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor grayColor];
    
    //layout布局类
    self.layout = [[CircleLayout alloc] init];
    
    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:self.layout];
    
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    [self.collectionView registerClass:[ImageCollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
    [self.view addSubview:self.collectionView];
}

//返回分区个数
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}

//返回每个分区的item个数
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 20;
}


//返回每个item
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 255.0 green:arc4random() % 255 / 255.0 blue:arc4random() % 250 / 250.0 alpha:1];
    cell.layer.cornerRadius = cell.frame.size.width / 2;
    cell.layer.masksToBounds = YES;
    return cell;
}

@end


UICollectionView的基础大概就是这些了,通过这些方法我们应该就可以稍微实现我们自己心中预想的效果了。
博客参考我学姐的博客:iOS—UICollectionView的简单使用
Good Lucky!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/994516.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存