美文网首页
iOS学习笔记之UICollectionView小结

iOS学习笔记之UICollectionView小结

作者: Kity_Pei | 来源:发表于2018-10-18 18:08 被阅读0次

    这里就是对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:如有遗漏的地方,还烦请大神指正补充。

    相关文章

      网友评论

          本文标题:iOS学习笔记之UICollectionView小结

          本文链接:https://www.haomeiwen.com/subject/wvtijftx.html