UICollectionView详解三:UICollectionViewLayout

上一节中,我介绍了UICollectionViewFlowLayout的使用,它主要是使用在流式布局中的,但对于某些复杂的布局,UICollectionViewFlowLayout就不起作用了。这个时候,我们可以考虑使用UICollectionViewLayout。 UICollectionViewFlowLayout是继承自UICollectionViewLayout,并且拥有自己的流式特性。对于一些复杂的效果,我们完全可以自定义UICollectionViewLayout来实现。

这一节,我就介绍使用UICollectionViewLayout来实现不同布局的来回切换。最终效果图如下:

样式1:

样式2:

我们可以通过点击屏幕的空白区域来回的切换上面两种效果,并且切换过程中,动画效果非常的流畅。

主界面的代码和以前一样,设置数据源;初始化UICollectionView;注册UICollectionViewCell;代码如下:

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

static NSString *const identifer = @"ImageCell";

@implementation ViewController

-(NSMutableArray *)images {
    if (!_images) {
        _images = [NSMutableArray array];
        for (int i=1;i<=8;i++) {
            [_images addObject:[NSString stringWithFormat:@"%d.jpg",i]];
        }
    }
    return _images;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    CGRect rect = CGRectMake(0, 150, self.view.frame.size.width,400);
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:rect collectionViewLayout:[[LFStackLayout alloc] init]];
    collectionView.dataSource = self;
    collectionView.delegate = self;

    // 注册collectionView(因为是从xib中加载cell的,所以registerNib)
    [collectionView registerNib:[UINib nibWithNibName:@"ImageCell" bundle:nil] forCellWithReuseIdentifier:identifer];

    [self.view addSubview:collectionView];

    self.collectionView = collectionView;
}

#pragma mark - 点击屏幕空白处,切换布局模式
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if ([self.collectionView.collectionViewLayout isKindOfClass:[LFStackLayout class]]) {
        [self.collectionView setCollectionViewLayout:[[LFCircleLayout alloc] init] animated:YES];
    } else {
        [self.collectionView setCollectionViewLayout:[[LFStackLayout alloc] init] animated:YES];
    }
}

#pragma mark - delegate
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return  self.images.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    ImageCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifer forIndexPath:indexPath];

    cell.iconName = self.images[indexPath.item];

    return cell;
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    // 1. 删除模型数据
    [self.images removeObjectAtIndex:indexPath.item];

    // 2. 删除UI元素
    [collectionView deleteItemsAtIndexPaths:@[indexPath]];
}

关键代码就是这一段,用来实现“堆叠布局”和“圆形布局”的自由切换。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if ([self.collectionView.collectionViewLayout isKindOfClass:[LFStackLayout class]]) {
        [self.collectionView setCollectionViewLayout:[[LFCircleLayout alloc] init] animated:YES];
    } else {
        [self.collectionView setCollectionViewLayout:[[LFStackLayout alloc] init] animated:YES];
    }
}

这里,我就“圆形布局”进行说明,自定义LFCircleLayout, 并且继承自UICollectionViewLayout

@interface LFCircleLayout : UICollectionViewLayout

@end

UICollectionViewFlowLayout是可以直接获取到所有Item的FlowLayout,然后对各自的layout进行调整;而UICollectionViewLayout完全需要自定义,来满足自己的需求。主要是下面的代码,重写父类的 layoutAttributesForElementsInRect方法,返回的数组对象就是自定义layout的集合。当获取到这个集合后,UICollectionView就会对每一个Item进行自动调节。

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
     ...
}

自定义LFCircleLayout中实现的layoutAttributesForElementsInRect方法如下:

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *array = [NSMutableArray array];
    NSInteger count = [self.collectionView numberOfItemsInSection:0];

    for (int i=0; i<count; i++) {
        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
        [array addObject:attrs];
    }

    return array;
}

代码中定义了一个方法:layoutAttributesForItemAtIndexPath, 专门用来设置每个Item的layout,完成“圆形布局”。代码如下:

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attrs.size = CGSizeMake(60, 60);

    // 第几个Item
    NSInteger index = indexPath.item;

    // 半径100
    CGFloat radius = 100;

    // 圆心
    CGFloat circleX = self.collectionView.frame.size.width * 0.5;
    CGFloat circleY = self.collectionView.frame.size.height * 0.5;

    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    CGFloat singleItemAngle = 360.0 / count;

    // 计算各个环绕的图片center
    attrs.center = CGPointMake(circleX + radius * cosf(kCalcAngle(singleItemAngle * index)), circleY - radius * sinf(kCalcAngle(singleItemAngle * index)));

    return attrs;
}

代码中关于计算弧度的方法,我定义了一个宏来处理。

#define kCalcAngle(x) x * M_PI / 180.0

“堆叠布局”我就不做介绍了,基本类似,主要代码如下:

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attrs.size = CGSizeMake(150, 150);
    attrs.center = CGPointMake(self.collectionView.frame.size.width * 0.5, self.collectionView.frame.size.height * 0.5);

    NSInteger index = indexPath.item;
    CGFloat angles[] ={0,15,30,45,60};
    NSInteger count = [self.collectionView numberOfItemsInSection:0];

    if (index >= 5) {
        attrs.hidden = YES;
    } else {
        attrs.transform = CGAffineTransformMakeRotation(kCalcAngle(angles[index]));
        attrs.zIndex = count - index;
    }

    return attrs;
}

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *array = [NSMutableArray array];
    NSInteger count = [self.collectionView numberOfItemsInSection:0];

    for (int i=0; i<count; i++) {
        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
        [array addObject:attrs];
    }

    return array;
}
时间: 2024-10-09 18:20:40

UICollectionView详解三:UICollectionViewLayout的相关文章

iOS开发——UI篇OC篇&amp;UICollectionView详解+实例

UICollectionView详解+实例 实现步骤: 一.新建两个类 1.继承自UIScrollView的子类,比如HMWaterflowView * 瀑布流显示控件,用来显示所有的瀑布流数据 2.继承自UIView的子类,比如HMWaterflowViewCell * 代表着瀑布流数据中的一个单元(一个格子) 3.总结 HMWaterflowView和HMWaterflowViewCell的关系实际上类似于 UITableView和UITableViewCell的关系 二.设计HMWater

iOS开发- UICollectionView详解+实例

iOS开发- UICollectionView详解+实例 本章通过先总体介绍UICollectionView及其常用方法,再结合一个实例,了解如何使用UICollectionView. UICollectionView 和 UICollectionViewController 类是iOS6 新引进的API,用于展示集合视图,布局更加灵活,可实现多列布局,用法类似于UITableView 和 UITableViewController 类. 使用UICollectionView 必须实现UICol

UICollectionView详解

今天,将和大家一起学习UICollectionView,UIcollectionView自出来后,一直受追捧,确实好用.今天有朋友问我如何添加heardView,我简单的回答:tableview如何添加,那么CollectionView就怎么添加,后来经过自己实验发现确实不是那回事,所以列出一些自己犯的错误,供大家参考. 1.首先实例化一个 UICollectionViewFlowLayout因为要设置item,itemSize,等一些属性. UICollectionViewFlowLayout

spark2.x由浅入深深到底系列六之RDD java api详解三

学习任何spark知识点之前请先正确理解spark,可以参考:正确理解spark 本文详细介绍了spark key-value类型的rdd java api 一.key-value类型的RDD的创建方式 1.sparkContext.parallelizePairs JavaPairRDD<String, Integer> javaPairRDD =         sc.parallelizePairs(Arrays.asList(new Tuple2("test", 3

php学习之道:WSDL详解(三)

通过声明方式定义绑定(binding)属性 如果你在服务中采用SOAP binding,你可以使用JAX-WS来指定一定数量的属性binding.这些属性指定对应你在WSDL中指定的属性.某些设置,比如参数类型,可以约束你实现的方法,这些设置也影响声明的效用. @SOAPBinding声明,定义在javax.jws.soap.SOAPBinding接口中.它提供发布时的SOAP Binding细节.如果@SOAPBinding没有被指定,则用缺省的doc/literal SOAPBinding.

UINavigationController详解三(转)ToolBar

原文出自:http://blog.csdn.net/totogo2010/article/details/7682641,特别感谢. 1.显示Toolbar  在RootViewController.m的- (void)viewDidLoad方法中添加代码,这样Toobar就显示出来了. [cpp] view plaincopy [self.navigationController  setToolbarHidden:NO animated:YES]; 2.在ToolBar上添加UIBarBut

logback -- 配置详解 -- 三 -- &lt;encoder&gt;

附: logback.xml实例 logback -- 配置详解 -- 一 -- <configuration>及子节点 logback -- 配置详解 -- 二 -- <appender> logback -- 配置详解 -- 三 -- <encoder> logback -- 配置详解 -- 四 -- <filter> -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

Android基础入门教程——8.3.6 Paint API之—— Xfermode与PorterDuff详解(三)

Android基础入门教程--8.3.6 Paint API之-- Xfermode与PorterDuff详解(三) 标签(空格分隔): Android基础入门教程 本节引言: 上一节,我们学习了Xfermode中的三儿子:PorterDuffXfermode构造方法中的为一个参数: PorterDuff.Mode,我们在观看了16种图片混排模式后,又自己写代码来验证了一下文档中 18种不同的混排模式,18种是新增了ADD和OVERLAY两种模式!当然,仅仅验证知道是不够的, 本节我们来写个例子

LinearLayout详解三:LayoutInflater创建View过程分析

项目人力资源管理主要有以下几个过程: 编制人力资源管理计划 组建项目团队 建设项目团队 项目团队管理 编制人力资源管理计划 根据什么来编? 直观点就是你要干什么事?干这些事有哪些制约? 这个说起来好像和没说一样,但就我自己做的一些项目来说,有以下困难: 1> 项目前期需求是不具体不明确的 这样直接导致你做项目计划时WBS也是不明确的,进而你细化不了活动,自然你也没法 明确活动需要什么样的人. 这个时候怎么办? 就我个人而言,有2种员工很喜欢: 1> 数学思维强的, 注意不是会做高数题,是指给你