前言

这几天有时间看了下UICollectionView的东西,才发觉它真的非常强大,很有必要好好学习学习。以前虽然用过几次,但没有系统的整理总结过。这两天我为UICollectionView做一个比较全面的整理。包括基本使用自定义布局自定义插入删除动画自定义转场动画等几部分。好了,开始。

UICollectionView相对于UITableView可以说是青出于蓝而胜于蓝,它和UITableView很相似,但它要更加强大。
UITableView的布局形式比较单一,局限于行列表,而UICollectionView的强大之处在于把视图布局分离出来成为一个独立的类,你想实现怎样的视图布局,就子类化这个类并在其中实现。

UICollectionView基础

  • UICollectionViewFlowLayout:视图布局对象(流视图:一行排满,自动排到下行),继承自UICollectionViewLayout。
    UICollectionViewLayout有个collectionView属性,
    所有的视图布局对象都继承自UICollectionViewLayout。若我们要自定义布局对象,我们一般继承UICollectionViewFlowLayout就可以了。
  • 需要实现三个协议;UICollectionViewDataSource(数据源)、UICollectionViewDelegateFlowLayout(视图布局)、UICollectionViewDelegate。
    可以看得出,除了视图布局,UICollectionView几乎和UITableView一样,但这也正是它的强大之处。
1.创建UICollectionView视图
- (void)loadCollectionView
{
_customLayout = [[CustomCollectionViewLayout alloc] init]; // 自定义的布局对象
_collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:_customLayout];
_collectionView.backgroundColor = [UIColor whiteColor];
_collectionView.dataSource = self;
_collectionView.delegate = self;
[self.view addSubview:_collectionView]; // 注册cell、sectionHeader、sectionFooter
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:cellId];
[_collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:headerId];
[_collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:footerId];
}

需要注意的是这几行代码的位置,及const的位置。(我经常搞乱)

@implementation YWViewController

// 注意const的位置
static NSString *const cellId = @"cellId";
static NSString *const headerId = @"headerId";
static NSString *const footerId = @"footerId"; - (void)viewDidLoad
{
2.实现UICollectionViewDataSource的几个代理方法
#pragma mark ---- UICollectionViewDataSource

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
} - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return _section0Array.count;
} - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [_collectionView dequeueReusableCellWithReuseIdentifier:cellId forIndexPath:indexPath];
cell.backgroundColor = [UIColor purpleColor]; return cell;
} // 和UITableView类似,UICollectionView也可设置段头段尾
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{ if([kind isEqualToString:UICollectionElementKindSectionHeader])
{
UICollectionReusableView *headerView = [_collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:headerId forIndexPath:indexPath];
if(headerView == nil)
{
headerView = [[UICollectionReusableView alloc] init];
}
headerView.backgroundColor = [UIColor grayColor]; return headerView;
}
else if([kind isEqualToString:UICollectionElementKindSectionFooter])
{
UICollectionReusableView *footerView = [_collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:footerId forIndexPath:indexPath];
if(footerView == nil)
{
footerView = [[UICollectionReusableView alloc] init];
}
footerView.backgroundColor = [UIColor lightGrayColor]; return footerView;
} return nil;
} - (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
} - (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath
{ } #pragma mark ---- UICollectionViewDelegateFlowLayout - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return (CGSize){cellWidth,cellWidth};
} - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
return UIEdgeInsetsMake(5, 5, 5, 5);
} - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
return 5.f;
} - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
return 5.f;
} - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
return (CGSize){ScreenWidth,44};
} - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section
{
return (CGSize){ScreenWidth,22};
} #pragma mark ---- UICollectionViewDelegate - (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
} // 点击高亮
- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
cell.backgroundColor = [UIColor greenColor];
} // 选中某item
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{ } // 长按某item,弹出copy和paste的菜单
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
} // 使copy和paste有效
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender
{
if ([NSStringFromSelector(action) isEqualToString:@"copy:"] || [NSStringFromSelector(action) isEqualToString:@"paste:"])
{
return YES;
} return NO;
} //
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender
{
if([NSStringFromSelector(action) isEqualToString:@"copy:"])
{
// NSLog(@"-------------执行拷贝-------------");
[_collectionView performBatchUpdates:^{
[_section0Array removeObjectAtIndex:indexPath.row];
[_collectionView deleteItemsAtIndexPaths:@[indexPath]];
} completion:nil];
}
else if([NSStringFromSelector(action) isEqualToString:@"paste:"])
{
NSLog(@"-------------执行粘贴-------------");
}
}

UICollectionView自定义布局

要自定义UICollectionView布局,就要子类化UICollectionViewLayout,然后重写它的一些方法以达到我们自定义布局的需求。下来我们来看看UICollectionViewLayout类里一些比较重要的方法:

  • - (void)prepareLayout;为layout显示做准备工作,你可以在该方法里设置一些属性。
  • - (CGSize)collectionViewContentSize;返回layout的size。
  • - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;返回在collectionView的可见范围内(bounds)所有item对应的layoutAttrure对象装成的数组。collectionView的每个item都对应一个专门的UICollectionViewLayoutAttributes类型的对象来表示该item的一些属性,比如bounds,size,transform,alpha等。
  • - (UICollectionViewLayoutAttributes )layoutAttributesForItemAtIndexPath:(NSIndexPath )indexPath;传入indexPath,返回该indexPath对应的layoutAtture对象。
  • - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds; 当当前layout的布局发生变动时,是否重写加载该layout。默认返回NO,若返回YES,则重新执行这俩方法:
    • - (void)prepareLayout;
    • - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;
  • - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity;返回layout“最终”的偏移量,何谓“最终”,手指离开屏幕时layout的偏移量不是最终的,因为它有惯性,当它停止时才是“最终”偏移量。

下面这两个方法一般用于自定义插入删除时的动画,后面再说。

  • - (UICollectionViewLayoutAttributes )initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath )itemIndexPath;

  • - (nullable UICollectionViewLayoutAttributes )finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath )itemIndexPath;

本Demo的代码虽然子类化了UICollectionViewLayout,但是主要是用于自定义插入删除动画,所以本段没什么代码展示。


UICollectionView插入删除的操作及动画

插入删除的操作

添加在哪触发:

    UIBarButtonItem *btnItem = [[UIBarButtonItem alloc] initWithTitle:@"添加"
style:UIBarButtonItemStylePlain
target:self
action:@selector(addItemBtnClick:)]; self.navigationItem.rightBarButtonItem = btnItem;

添加的实现:

// 添加(插入item)
- (void)addItemBtnClick:(UIBarButtonItem *)btnItem
{
[_collectionView performBatchUpdates:^{
// 构造一个indexPath
NSIndexPath *indePath = [NSIndexPath indexPathForItem:_section0Array.count inSection:0];
[_collectionView insertItemsAtIndexPaths:@[indePath]]; // 然后在此indexPath处插入给collectionView插入一个item
[_section0Array addObject:@"x"]; // 保持collectionView的item和数据源一致
} completion:nil];
}

因为是练习Demo,所以暂时把删除的触发源写在了长按某Item弹出菜单的copy按钮里。实际中你可以自定义UICollectionViewCell,添加长按手势,长按抖动出现叉号,然后删除等,随你怎么做。

// copy and paste 的实现
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender
{
if([NSStringFromSelector(action) isEqualToString:@"copy:"])
{
// NSLog(@"-------------执行拷贝-------------");
[_collectionView performBatchUpdates:^{
[_section0Array removeObjectAtIndex:indexPath.row];
[_collectionView deleteItemsAtIndexPaths:@[indexPath]];
} completion:nil];
}
else if([NSStringFromSelector(action) isEqualToString:@"paste:"])
{
NSLog(@"-------------执行粘贴-------------");
}
}

插入删除的动画

上面已经提到了在UICollectionViewLayout类中有两个用于自定义动画的方法,两个方法分别表示动画的起始状态和终止状态,我们可以分别在方法里设置layoutAttrure来实现某种动画效果。

苹果选择了一种安全的途径去实现一个简单的淡入淡出动画作为所有布局的默认动画。如果你想实现自定义动画,最好的办法是子类化 UICollectionViewFlowLayout 并且在适当的地方实现你的动画。

一般来说,我们对布局属性从初始状态到结束状态进行线性插值来计算 collection view 的动画参数。然而,新插入或者删除的元素并没有最初或最终状态来进行插值。要计算这样的 cells 的动画,collection view 将通过 initialLayoutAttributesForAppearingItemAtIndexPath: 以及 finalLayoutAttributesForDisappearingItemAtIndexPath: 方法来询问其布局对象,以获取最初的和最后的属性。苹果默认的实现中,对于特定的某个 indexPath,返回的是它的通常的位置,但 alpha 值为 0.0,这就产生了一个淡入或淡出动画。

简而言之,就是苹果自带了插入删除时Item的淡入淡出的动画,若你想自定义更炫的动画,就子类化UICollectionViewFlowLayout类,并重写以下两个方法:

// 初始状态
- (nullable UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
UICollectionViewLayoutAttributes *attr = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
attr.center = CGPointMake(CGRectGetMidX(self.collectionView.bounds), CGRectGetMaxY(self.collectionView.bounds));
attr.transform = CGAffineTransformRotate(CGAffineTransformMakeScale(0.2, 0.2), M_PI); return attr;
} // 终结状态
- (nullable UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
UICollectionViewLayoutAttributes *attr = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
attr.alpha = 0.0f; return attr;
}

insert&delete.gif

UICollectionView的转场动画

http://objccn.io/issue-12-5/

UICollectionView——整体总结的更多相关文章

  1. iOS-UICollectionView快速构造/拖拽重排/轮播实现

    代码地址如下:http://www.demodashi.com/demo/11366.html 目录 UICollectionView的定义 UICollectionView快速构建GridView网 ...

  2. iOS6新特征:UICollectionView介绍

    http://blog.csdn.net/eqera/article/details/8134986 1.1. Collection View 全家福: UICollectionView, UITab ...

  3. UICollectionView介绍

    文章原出处未知,如有朋友知道,请告诉我,我会补上. 1.1. Collection View 全家福: UICollectionView, UITableView, NSCollectionView ...

  4. IOS 瀑布流UICollectionView实现

    IOS 瀑布流UICollectionView实现 在实现瀑布流之前先来看看瀑布流的雏形(此方法的雏形 UICollectionView) 对于UICollectionView我们有几点注意事项 它和 ...

  5. UICollectionView使用

    本文原文 原文转自 1.1. Collection View 全家福: UICollectionView, UITableView, NSCollectionView n   不直接等效于NSColl ...

  6. UICollectionView 简单的使用和注意事项

    UICollectionView 在创建的时候,要给它一个UICollectionViewFlowLayout (不然会崩溃),就像tableview一样,也要为它注册自定义的cell. UIColl ...

  7. iOS UICollectionView(转二)

    UICollectionView的布局是可以自己定义的,在这篇博客中先在上篇博客的基础上进行扩充,我们先使用UICollectionViewFlowLayout,然后好好的介绍一下UICollecti ...

  8. iOS 简易型标签的实现(UICollectionView)

    https://blog.csdn.net/sinat_39362502/article/details/80900984 2018年07月03日 16:49:05 Recorder_MZou 阅读数 ...

  9. UICollectionView官方使用示例代码研究

    注:这里是iOS6新特征汇总贴链接 iOS6新特征:参考资料和示例汇总 这个链接可以学习到UICollectionView的相关介绍:iOS6新特征:UICollectionView介绍 由于UICo ...

随机推荐

  1. P1452 Beauty Contes(旋转卡壳版)

    题目背景 此处省略1W字^ ^ 题目描述 贝茜在牛的选美比赛中赢得了冠军”牛世界小姐”.因此,贝西会参观N(2 < = N < = 50000)个农场来传播善意.世界将被表示成一个二维平面 ...

  2. 织梦CMS调用文章第一张图片(非缩略图)终极方法

    之前,网上流传了很多在织梦CMS中调用第一张图片的方法,但大体都一样.即删除缩略图字符串,并添加后缀.然而这种方法仅限于jpg图片或其他单独图片类的调用.如果一个站有png.JPG.gif等多种格式. ...

  3. css inline-block列表布局

    一.使用inline-block布局 二.多列布局方法二 <html><head> <meta charset="utf-8"> <tit ...

  4. Threading and Tasks in Chrome

    Threading and Tasks in Chrome Contents Overview Threads Tasks Prefer Sequences to Threads Posting a ...

  5. [NOI2008]志愿者招募 网络流 建模

    题目描述申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者.经过估算,这个项目需要N 天才能完成,其中第i 天 ...

  6. S-Nim POJ - 2960 Nim + SG函数

    Code: #include<cstdio> #include<algorithm> #include<string> #include<cstring> ...

  7. JDBC连接SQL Server遇到的问题

    需要使用到微软的JDBC sql server的驱动类,去官网下载jar包 使用的URL模式:"jdbc:sqlserver:地址:端口//;databaseName=YourDatabas ...

  8. Python爬虫基础示例

    使用pip安装相关依赖: pip install requests pip install bs4 安装成功提示:Successfully installed *... 爬取中国天气网数据示例代码: ...

  9. WHU 1540 Fibonacci 递推

    武大邀请赛的网络预选赛,就去做了个签到题,居然连这个递推都没推出来,真是惭愧. 而且好久没写矩阵乘法了,来回顾一下. 题意: 求Fibonacci数列的,前n项立方和. 思路: 可以求得一下递推公式: ...

  10. 熟悉Android开发不得不知道的技巧

    博客出自:http://blog.csdn.net/liuxian13183,转载注明出处! All Rights Reserved ! 1.用Eclipse插件将文件批量编码如GBK-UTF-8 用 ...