iOS学习笔记之UICollectionView小结


这里就是对UICollectionView的一个个人总结,不喜勿喷,如有不妥之处,望请指正🙏🙏🙏


1.简介

UICollectionView是iOS 6新引进的API,用于展示集合视图,布局更加灵活,可以显示多列布局,具有高度定制内容展示样式的能力,用法类似UITableView和UITableViewController类似。

  • 重要组成部分
    • UICollectionViewCell

      • 生命周期
      iOS 6 ~ iOS 9
      

      当屏幕外有一个cell准备划入屏幕即将显示的时候,会将cell通过重用标识符从reuse队列里取出来,然后会调用func prepareForReuse()(cell上边缘马上进入屏幕的时候调用),这个方法会重制cell,再滑动的话,就会调用func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell方法了,这个方法里面就是开发者用data model填充cell,把cell返回给系统,当cell马上就要进入屏幕的时候,就会调用func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath),这时候就是App最后一次为cell进入屏幕做准备工作的机会了,执行完该方法,cell就进入屏幕中了。当滑动屏幕,cell完全离开屏幕之后,就会调用func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath)方法。

      iOS 10 ~  *
      

      当滑动屏幕的时候,需要一个cell时,会将cell痛过重用标识符从reuse队列取出来,并调用func prepareForReuse()方法(当cell还没有进入屏幕的时候,就已经提前调用了,这是跟iOS 10 之前的不同之处,也就是说iOS 10的时候cell的整个生命周期都被提前了),再滑动的时候,就会跟iOS 10 之前一样去调用cellForItemAtIndexPath去创建以及填充data model,并把cell返回给系统,同样的因为在之前生命周期提前了,所以这个方法也较之iOS 10 之前调用的要早,不同之处在后面,iOS 10 在调用func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)方法时,遵循的原则也就是什么时候显示cell,什么时候再去调用,后面基本和iOS10 之前一致了,还有个重大的不同之处就是iOS 10会把滑出屏幕的Cell保持一段时间,当用户滑动太快,想重新滑动回去的时候,cell不需要重新走通过重用标识符获取什么之类的路了,直接调用func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)这个方法就行了。

    • UICollectionViewFlowLayout

      我们可以通过修改UICollectionViewFlowLayout的属性来实现一些简单的UICollectionView的样式。

      • 属性介绍
      // UICollectionViewCell之间的最小行间距,默认为0,实际值只能比该值大
      var minimumLineSpacing: CGFloat 
      // UICollectionViewCell之间的最小列间距,实际值只能比这个值大
      var minimumInteritemSpacing: CGFloat  
      // UICollectionViewCell的大小
      var itemSize: CGSize  
      // 从iOS 8 开始支持的,预估cell的大小,用于适应动态计算item的大小,默认为CGSize.zero,如果想用这个属性的话,必须在自定义cell中实现`func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes`方法
      var estimatedItemSize: CGSize 
      // UICollectionView的滚动方向,默认为垂直方向
      open var scrollDirection: UICollectionView.ScrollDirection 
      // UICollectionView的Header大小
      open var headerReferenceSize: CGSize  
      // UICollectionView的Footer大小
      open var footerReferenceSize: CGSize  
      // UICollectionView中区的内容偏移
      open var sectionInset: UIEdgeInsets 
      //  从iOS 9 开始,设置header或者footer是否悬浮,true为悬浮,类似tableView的区头悬浮效果
      var sectionHeadersPinToVisibleBounds: Bool
      var sectionFootersPinToVisibleBounds: Bool
      
2.与UITableView的区别
  • 相同点

    1. 都是通过Delegates和Data Sources进行驱动的,因此使用的时候都必须实现数据源和代理协议方法;

    2. 在性能上都是利用重用标示来优化循环利用。

  • 不同点

    1. UITableView在初始化的时候只需要传入Frame,然后系统会帮助开发者布局UITableView的cell,不需要开发者而外的处理。而UICollectionView在初始化的时候必须传入布局样式(UICollectionViewLayout),然后系统根据布局样式来进行Cell的布局,当然系统也提供并实现了一个布局样式:UICollectionViewFlowLayout(大多数需求出来之后都不能直接用这个布局样式,,有时候更是麻烦的一腿,囧~);

    2. UITableView的滑动方向只能是垂直的(当然也能水平,但是还不如直接用UICollectionView替换),UICollectionView的滑动方向可以是垂直方向也可以是水平方向;

    3. UICollectionView的cell必须要先用重用标识符注册,而UITableView不用。

3.常用说明
  • UICollectionViewDelegate
支持版本:iOS 6.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool

支持版本:iOS 6.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath)

支持版本:iOS 6.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath)

支持版本:iOS 6.0 以上
用处:指定cell是否可选中
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool

支持版本:iOS 6.0 以上
用处:常用于用户在多选择情况下点击已经选中的单元格
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, shouldDeselectItemAt indexPath: IndexPath) -> Bool

支持版本:iOS 6.0 以上
用处:用于用户选中某一个单元格
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)

支持版本:iOS 6.0 以上
用处:用于用户取消选中某一单元格
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath)

支持版本:iOS 8.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)

支持版本:iOS 8.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath)

支持版本:iOS 6.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath)

支持版本:iOS 6.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, didEndDisplayingSupplementaryView view: UICollectionReusableView, forElementOfKind elementKind: String, at indexPath: IndexPath)

支持版本:iOS 7.0 以上
用处:当需要转换布局的时候调用
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, transitionLayoutForOldLayout fromLayout: UICollectionViewLayout, newLayout toLayout: UICollectionViewLayout) -> UICollectionViewTransitionLayout

支持版本:iOS 9.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, canFocusItemAt indexPath: IndexPath) -> Bool

支持版本:iOS 9.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, shouldUpdateFocusIn context: UICollectionViewFocusUpdateContext) -> Bool

支持版本:iOS 9.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, didUpdateFocusIn context: UICollectionViewFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator)

支持版本:iOS 9.0 以上
用处:
是否必须实现:否

func indexPathForPreferredFocusedView(in collectionView: UICollectionView) -> IndexPath?

支持版本:iOS 9.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, targetIndexPathForMoveFromItemAt originalIndexPath: IndexPath, toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath

支持版本:iOS 9.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, targetContentOffsetForProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint // customize the content offset to be applied during transition or update animations

支持版本:iOS 11.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, shouldSpringLoadItemAt indexPath: IndexPath, with context: UISpringLoadedInteractionContext) -> Bool

  • UICollectionViewDataSource(UICollectionView的数据源方法)
支持版本:iOS 6.0 以上
用处:确定某个区有多少个单元格
是否必须实现:是

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int

支持版本:iOS 6.0 以上
用处:根据类型返回注册过的cell,返回的cell必须是是注册过的,且重用标识符与注册时的要一模一样。
是否必须实现:是

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell

支持版本:iOS 6.0 以上
用处:确定当前CollectionView有多少个区,默认为1
是否必须实现:否

func numberOfSections(in collectionView: UICollectionView) -> Int

支持版本:iOS 6.0 以上
用处:根据类型返回注册过的头或者尾视图,返回的视图必须是是注册过的,且重用标识符与注册时的要一模一样。
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView

支持版本:iOS 9.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool

支持版本:iOS 9.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath)

支持版本:iOS 6.0 以上
用处:
是否必须实现:否

func indexTitles(for collectionView: UICollectionView) -> [String]?

支持版本:iOS 6.0 以上
用处:
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, indexPathForIndexTitle title: String, at index: Int) -> IndexPath

  • UICollectionViewDelegateFlowLayout
支持版本:iOS 6.0 以上
用处:单元格的大小
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize

支持版本:iOS 6.0 以上
用处:设置指定区内的内边距
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets

支持版本:iOS 6.0 以上
用处:设置指定区内的cell的最小行间距
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat

支持版本:iOS 6.0 以上
用处:设置指定区内的cell的最小列间距
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat

支持版本:iOS 6.0 以上
用处:设置指定区的header的大小
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize

支持版本:iOS 6.0 以上
用处:设置指定区的footer的大小
是否必须实现:否

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize

4.自定义UICollectionViewLayout
4.1 简单说明

在实际开发中,作为iOS开发者可能在用到UICollectionView的时候,大多数都要使用自定义UICollectionViewLayout来定制一些极其能让用户接受的展示UI,确实如此。
在自定义UICollectionViewLayout的时候我觉得可以分成3个步骤:
1.重写UICollectionViewLayout的prepareLayout,在该方法里事先将计算好的一些必要的布局信息,并且存储起来;
2.基于prepareLayout方法中的布局信息,使用collectionViewContentSize方法返回UICollectionView的内容尺寸;
3.使用layoutAttributesForElementsInRect方法返回指定的区域的cell、Supplementary View和Decoration View的布局属性。

4.2 自定义UICollectionViewLayout的Demo

稍后补充Demo以及效果图


5.开发过程中遇到的问题以及解决方案

5.1 问题一:

报错信息Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'UICollectionView must be initialized with a non-nil layout parameter'

错误原因分析:
UICollectionView缺少布局对象。
解决方案
给UICollectionView指定布局对象(UICollectionViewLayout)
5.2 问题二:

报错信息Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'could not dequeue a view of kind: UICollectionElementKindCell with identifier cell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard

错误原因分析:
没有注册cell,系统不知道按照哪种方式去创建cell。
解决方案
给CollectionView注册cell,可以通过之前几种方法注册cell。

open func register(_ cellClass: AnyClass?, forCellWithReuseIdentifier identifier: String)
open func register(_ nib: UINib?, forCellWithReuseIdentifier identifier: String)
open func register(_ viewClass: AnyClass?, forSupplementaryViewOfKind elementKind: String, withReuseIdentifier identifier: String)
open func register(_ nib: UINib?, forSupplementaryViewOfKind kind: String, withReuseIdentifier identifier: String)

5.3 问题三:

报错信息Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'the cell returned from -collectionView:cellForItemAtIndexPath: does not have a reuseIdentifier - cells must be retrieved by calling -dequeueReusableCellWithReuseIdentifier:forIndexPath:

错误原因分析:
在返回cell的时候,没有使用重用标识符,必须要通过dequeueReusableCellWithReuseIdentifier:forIndexPath:方法来创建。

解决方案
在调用func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell方式时,要通过collectionView注册cell的时使用的重用标识符来获取cell。

5.4 问题四:

报错信息Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'invalid nib registered for identifier (ViewCell) - nib must contain exactly one top level object which must be a UICollectionReusableView instance'

错误原因分析:
在使用nib来注册cell的时候发现,在nib文件中有两个或者以上的cell。
解决方案
每一个UICollectionViewCell的Nib文件有且只能有一个cell样式,分离多余的Cell,只保留Nib中只有一个cell样式。

5.5 问题五

报错信息Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'the view returned from -collectionView:viewForSupplementaryElementOfKind:atIndexPath (UICollectionElementKindSectionHeader,<NSIndexPath: 0xa960f426cdfc3d50> {length = 2, path = 0 - 0}) was not retrieved by calling -dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath: or is nil (<faceslink.PbCommitFooterView: 0x10f9e5a50; baseClass = UICollectionReusableView; frame = (0 0; 0 0); layer = <CALayer: 0x2824fd8e0>>)'

错误原因分析:
出现这种情况的原因是实现了UICollectionElementKindSectionHeader大小的代理,但是未注册UICollectionElementKindSectionHeader,所以colleciontView未找到UICollectionElementKindSectionHeader。
解决方案
注册UICollectionElementKindSectionHeader


😊😊😊😊😊😊
关于UICollectionView开发过程中遇到的问题,会在后续工作中继续发现,继续补充。


Tip:如有遗漏的地方,还烦请大神指正补充。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容