UICollectionView——整体总结

前言

这几天有时间看了下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:UIBarButtonItemStylePlaintarget: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/

转载于:https://www.cnblogs.com/Free-Thinker/p/6737701.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/269580.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java Class对象返回的都是指向同一个java堆地址上的Class引用

一旦类被加载了到了内存中,那么不论通过哪种方式获得该类的Class对象,它们返回的都是指向同一个java堆地址上的Class引用。jvm不会创建两个相同类型的Class对象 public class Cat {static {System.out.println("wwwww");} }class T1 {public s…

matlab基础与实例教程,MATLAB基础与实例教程

系统全面,实例丰富 考虑到Matlab进行仿真和运算分析时的基础知识和实践操作,讲解从基础的变量、函数、数据类型等入手,涉及到数学分析、图形可视化、Simulink仿真、文件读写等,全面地介绍了Matlab的分析方法。 精心编排&#xff0…

Android开发技巧——ViewPager加View情况封装PagerAdapter的实现类

ViewPager是Android的support库中的一个控件。 ViewPager Fragment的使用,已经有FragmentAdapter的实现可以帮助我们快速进行开发了; ViewPager View都要自己去继承PagerAdapter并实现,重写那些写过一次又一次的方法,于是以下对…

近300万记录的论坛还用这个分页存储过程

http://www2.gliet.edu.cn/club2/bbs/list_new_today.asp用于列出今日最新贴的。有个想不明的问题是为什么DeclareSqlText Varchar(1000)声明为1000个字符大了,但超过255字符之后exec(SqlText)就会出错。自动把后面的cut掉了?ALTERPROCEDUREbbsListNewTo…

php横排代码,20行代码原生js实现文字横向轮播

20行代码实现文字横向轮播效果1 页面布局代码恭喜793765***获得 50元超市充值卡卡奖励恭喜793765***获得 50元超市充值卡卡奖励恭喜793765***获得 50元超市充值卡卡奖励恭喜793765***获得 50元超市充值卡卡奖励恭喜793765***获得 50元超市充值卡卡奖励恭喜793765***获得 50元超…

JAVA项目中classpath路径详解

classpath是什么路径? 1.classpath指的是类路径,也就是编译之后的target文件夹下的WEB-INF/class文件夹。 2.resources文件夹存放的是各种配置文件,当项目被编译时resources下的所有配置文件均被放在 WEB-INF/class文件夹下。也就是类路径下…

将一张表的主键(ID)重置为从1开始自增排列

如果你有一张表,你的主键是ID,然后由于测来测去的原因,你的ID不是从1开始连续的自增了。 终于有一天,使用这张表的某个系统要导入正式数据了,强迫症这时候就表现的明显了,浑身不自在, 这时候你就…

涉险获胜和兵不血刃

两场比赛,两种心情。昨天的羽毛球混双决赛,除了第一局以外,中国队一直处于落后追赶的局面,让人看得心惊肉跳,手心也直冒汗。好在张军、高崚的斗志始终旺盛,互相鼓励,最终拖垮了英国选手而卫冕成…

matlab 图像的色彩强度,图像色彩增强的问题,,,小白给跪,

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼img_srcrgb2gray(img_src);img_srcdouble(img_src);[M,N]size(img_src);L256;for i1:Mfor j1;Nif img_src(i,j)R(i,j)0;G(i,j)4*img_src(i,j);B(i,j)L;elseif img_src(i,j)<L/2R(i,j)0;G(i,j)L;B(i,j)4*img_src(i,j)2*L;elseif …

Idea运行项目报错:java.lang.OutOfMemoryError: Java heap space/ java.lang.OutOfMemoryError: GC overhead 解决方法

问题 使用Idea运行代码量比较多的项目时&#xff0c;会报错&#xff1a;java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: GC overhead limit exceeded java.lang.OutOfMemoryError: GC overhead limit exceeded 仔细看了整个报错过程&#xff0c;堆…

数据库读写分离(aop方式完整实现)

http://blog.csdn.net/machunlin2010/article/details/46471983转载于:https://www.cnblogs.com/baihaojie/p/6742171.html

C#设计模式(10)-Adapter Pattern

内容篇幅较长,请点击这里阅读全文

Leaning Method

001用分布在两个地方的两台办公电脑开发一个项目&#xff0c;计划使用gitee同步代码。具体应该怎么操作&#xff1f; 要使用 Gitee 同步代码&#xff0c;你可以按照以下步骤进行操作&#xff1a; 在两台办公电脑上都安装 Git 客户端&#xff0c;并在 Gitee 上创建一个项目仓库…

wamp php文件怎么创建数据库,phpmyadmin怎么创建数据库

phpmyadmin怎么创建数据库?1、首先登陆phpmyadmin&#xff0c;有两种方法&#xff0c;第一钟是直接点击wampserver图标&#xff0c;再点击phpmyadmin即可登陆进来是最常用的方法&#xff1b;其次是在浏览器地址中输入http://localhost:8080/phpmyadmin(注意8080是端口号&#…

maven的三大生命周期

一、Maven的生命周期 Maven的生命周期就是对所有的构建过程进行抽象和统一。包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有的构建步骤。 Maven的生命周期是抽象的&#xff0c;即生命周期不做任何实际的工作&#xff0c;实际任务由插…

文言的理解 —— 古时的别称(地名)

圣女果&#xff08;cherry tomato&#xff09;&#xff0c;也就是小番茄&#xff1b;- 车厘子&#xff0c;也即是樱桃&#xff1b;- 凤梨&#xff0c;也就是菠萝&#xff1b;新疆的称呼在清以后才有&#xff0c;清以前统称为西域&#xff1b;1. 国家篇 东南亚&#xff1a; 暹罗…

php $start_date-sub(,PHP DateTime类常用方法总结

实例化&#xff1a;$date new DateTime();$date new DateTime(2018-01-01);$date new DateTime(tomorrow);$date new DateTime(2 day);$date new DateTime(1408950651);设置日期时间&#xff1a;$date->setTimestamp(1514739661);$date->setDate(2020, 8, 8);$date-…

static{}静态代码块与{}普通代码块和构造代码块之间的区别

public class TestStatic {{System.out.println("构造代码块");}TestStatic() {System.out.println("构造函数代码块");}//类级别的初始化&#xff0c;只是在类加载的时候做一次static {System.out.println("static静态代码块");} } 1.静态代码块…

多线程编程(三)--创建线程之Thread VS Runnable

前面写过一篇基础的创建多线程的博文&#xff1a; 那么本篇博文主要来对照一下这两种创建线程的差别。 继承Thread类&#xff1a; 还拿上篇博客的样例来说&#xff1a; 四个线程各自卖各自的票&#xff0c;说明四个线程之间没有共享&#xff0c;是独立的线程。每一个线程平等。…

wamp php imagick,如何在wamp for Windows中安装和启用Imagick扩展 php psd 生成缩略图

之所以写这篇文章就是希望后面的人少踩坑&#xff0c; 当时有个需求是psd生成缩略图网上相关的文章寥寥无几&#xff0c;然后很多文章都是缺胳膊缺腿&#xff0c;然后找了半天在国外找到一篇文章&#xff0c;不过这篇文章是xamp集成环境的 我是wamp集成环境 &#xff0c; 但是套…