乐趣区

关于swift:CollectionView详解

最近将 UICollectionView 进行了一个全面的学习及总结,参考了网上大量的文章,把官网文档进行了大略翻译,最初有个小 Demo。

UICollectionView 基础知识

collection view 是使用一个灵便多变的布局出现一系列有序数据项的一种办法。collection view 最通常的应用应用像网格状排列来出现数据项,然而 iOS 的 collection view 的能力不仅限于行和列。应用 collection views, 视觉元素的准确布局可通过子类化定义并被动静扭转。所以你能够实现网格,栈,圆形布局,动静扭转布局,或任何你能够设想的排列布局。

Collection View 是由多个对象合作而成

在下面能够看出,一个 CollectionView 视图从 data source 中获取数据信息,data source 和 delegate 来治理具体的单元对象,包含选中、未选中、高亮、未高亮等状态。Layout 决定了每个 cell 之间的边界及布局信息,并通过 layoutattributes 来决定每个 cell 的属性。

ReusableView 采纳的可重用 cell 的办法,能够提高效率, 反对三种不同的可重用的 view

  • cell 展现了 collection view 的次要内容,cell 的内容来自你的数据对象,每个 cell 必须是 UICollectionViewCell 类的实例。cell 对象能够治理本人的选中和高亮状态
  • Supplementary views 展现了对于 section 的信息,同样也是数据驱动的,是可选的。
  • Decoration views 装璜视图,不与数据相干,齐全属于 layout 对象。

Layout 对象管制视觉显示

layout object 决定着 cell 的大小,地位还有其余显示相干的属性。
layout object 没有接触任何视图,它只是一个属性汇合。而没有任何限度,你能够随你所想。
layout object 不止能够管制视图的宽高尺寸,还能够管制视图之间的关系属性,透明度,3D,和他跟其余视图之间的可见性比照。

Collection Views Initiate Animations Automatically

collection views 在 cell 挪动、插入或删除的时候,会有默认的动画。能够自定义这些动画。

设计 Data Source 和 Delegate

每个 collection view 必须有一个 data source 的对象,是 app 展现的内容。必须提供 collection view 须要的信息,比方多少个 items 等。delegate 对象个别用于和内容的交互相干的。能够治理 cell 的高亮选中状态,除此之外还有额定的工作,比方自定义布局的 cell 的大小及距离 spacing 等。

用 Data Source 治理你的内容

data source 须要遵循 UICollectionViewDataSource 协定提供三个信息:

  • collection view 包含多少个 sections
  • 每个 section 包含多少个 items
  • 每个 cell 展现的内容

几个办法

  • 展现数量

    • collectionView(_:numberOfItemsInSection:)
    • numberOfSectionsInCollectionView(_:)
  • 展现 cell

    • collectionView(_:cellForItemAtIndexPath:)
    • collectionView(_:viewForSupplementaryElementOfKind:atIndexPath:)
  • cell 重排

    • collectionView(_:canMoveItemAtIndexPath:)
    • collectionView(_:moveItemAtIndexPath:toIndexPath:)

设计你的 Data Objects

一个无效的数据源应用节和元素来组织根底的数据对象。一个简略的解决方案就是如图一样,把数据源组织成一个嵌套数组,最外层数据蕴含一个或多个数据展现数据源中的节,每个节数据蕴含多个元素。当设计数据结构时,要思考性能问题,汇合视图拜访数据源仅是为了计算一共多少元素并获取以后屏幕所需的数据对象。如果布局对象只是依赖于数据对象,当数据对象蕴含上千条数据的时候,性能会受到大幅的影响。

通知汇合视图相干的数据源信息

当以下产生时,须要提供给汇合视图相干 data source

  • collection view 第一次显示的时候
  • 给 collection view 指定了不同的 data source
  • 明确调用了 reloadData 办法
  • collection view 的代理执行了 performBatchUpdates:completion: 办法, 或者任何挪动、插入、删除办法执行的时候
collectionView.performBatchUpdates({() -> Void in
            collectionView.insertItemsAtIndexPaths(insertIndexPaths)
            collectionView.moveItemAtIndexPath(currentIndexPath, toIndexPath: toIndexPath)
            }, completion: {(isFinish) -> Void in
        })

通过 numberOfSectionsInCollectionView: 来提供分组数量,可选的办法,默认是 1,应用 collectionView:numberOfItemsInSection: 来提供每个分组的 items 的数量。

配置单元格(cells)和增补视图(supplementa views)

要给单元提供内容,须要做的两件事:

  • 在 storyboard 文件中嵌入模板单元,(能够注册一个 class 或者 nib 文件)
  • 在数据源中,重用出列(dequeue)并且配置相应的 cell 单元

复用标识(Reuse identifiers)能够让注册多种类型的单元和增补视图成为可能。当申请一个视图或一个对象时,能够应用提供的索引门路决定想要应用哪种类型的视图和单元格,而后传递适当的重用标识给 dequeue 函数。

注册单元格和增补视图

两种形式注册单元格,在 storyboard 中注册或者手动代码注册。

  • 应用 storyboard 文件,拖拽 cell 或者 supplementary views 到 storyboard 中,并且配置相干属性,设置重用标识。
  • 应用手动代码的形式注册

    • 注册 cell,采纳 registerClass:forCellWithReuseIdentifier: 或者 registerNib:forCellWithReuseIdentifier: 办法来注册 cell
    • 注册 supplementary views,采纳registerClass:forSupplementaryViewOfKind:withReuseIdentifier: 或者 registerNib:forSupplementaryViewOfKind:withReuseIdentifier: 办法来注册

supplementary views 除了重用标识外,还有一个额定的标识,每个 layout 对象负责定义 supplementary view 反对的 kind。比方 UICollectionViewFlowLayout类反对两种类型 UICollectionElementKindSectionHeader UICollectionElementKindSectionFooter

留神:如果应用的自定义的 layouts, 须要本人定义 supplementary views 的类型。

重用和配置单元格和增补视图

data source 对象负责提供 cell 和 supplementary view 的内容,蕴含两个办法: collectionView:cellForItemAtIndexPath:collectionView:viewForSupplementaryElementOfKind:atIndexPath:, 实现两个办法的简略步骤:

  • 重用 cell 或 supplementary views

    • dequeueReusableCellWithReuseIdentifier:forIndexPath:
    • dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath:
  • 对于指定的 IndexPath 利用数据来配置 cell 或 view
  • 返回 view 或 cell

例子:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {let newCell = collectionView.dequeueReusableCellWithReuseIdentifier(MyCellID, forIndexPath: indexPath) as! MyCustomCell
         newCell.cellLabel.text = "Section:\(indexPath.section), Item:\(indexPath.item)"
        return newCell
    }

插入、删除、挪动 sections 和 items

插入、删除,挪动单个 section 或者 items,必须遵循两个步骤:

  • 在 datasource 对象中更新数据
  • 调用相应的插入、删除等办法

当插入、删除和挪动单个 item 的时候,collection view 的办法会对这些变动主动产生一个动画成果。如果想多个扭转共用一个动画,必须在 performBatchUpdates:completion: 办法的闭包里执行插入、删除、挪动等。

例子:

 collectionView.performBatchUpdates({let itemPaths = collectionView.indexPathsForSelectedItems()
            // 从 data source 中删除数据
            deleteItemFromDataSourceAtIndexPaths()
            collectionView.deleteItemsAtIndexPaths(itemPaths)
            }, completion: nil)

治理 cell 的选中、高亮状态

collection view 反对单个 item 选中,也能够配置为多个 item 选中或者禁止选中。当 selectedBackgroundView 中蕴含一个无效的 view 的时候,当 cell 是高亮或选中状态时会显示这个 view。此种形式能够扭转 cell 高亮或选中时的背景色彩。

collection views 的代理提供的一些办法:

  • collectionView:shouldSelectItemAtIndexPath:
  • collectionView:shouldDeselectItemAtIndexPath:
  • collectionView:didSelectItemAtIndexPath:
  • collectionView:didDeselectItemAtIndexPath:
  • collectionView:shouldHighlightItemAtIndexPath:
  • collectionView:didHighlightItemAtIndexPath:
  • collectionView:didUnhighlightItemAtIndexPath:

扭转 cell 背景色彩的一个例子

- (void)collectionView:(UICollectionView *)colView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath {UICollectionViewCell* cell = [colView cellForItemAtIndexPath:indexPath];
    cell.contentView.backgroundColor = [UIColor blueColor];
}
 
- (void)collectionView:(UICollectionView *)colView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath {UICollectionViewCell* cell = [colView cellForItemAtIndexPath:indexPath];
    cell.contentView.backgroundColor = nil;
}

高亮和选中区别

显示编辑按钮

当在 cell 中执行一个长按的手势,collection view 能够显示一个编辑按钮,能够用于剪切、粘贴、复制这个 cell。编辑按钮能被显示必须满足以下条件:

  • 代理必须实现三个办法

    • collectionView:shouldShowMenuForItemAtIndexPath: 是否显示菜单
    • collectionView:canPerformAction:forItemAtIndexPath:withSender: 菜单中哪些编辑操作能够显示
    • collectionView:performAction:forItemAtIndexPath:withSender: 对于显示的编辑操作怎么执行
  • 反对三种编辑操作 cut: copy: paste:

layouts 布局之间的转场

最简略的在两个布局之间转变的办法是 setCollectionViewLayout:animated:。如果须要管制转场动画或者制作可交互的转场动画,须要应用 UICollectionViewTransitionLayout 对象。

应用 UICollectionViewTransitionLayout 对象的步骤:

  • 应用 initWithCurrentLayout:nextLayout: 办法创立类
  • 周期性的批改 transitionProgress 属性的值,在扭转过渡的进度后,应用 collection view 的 “ 办法使布局有效。
  • collectionView:transitionLayoutForOldLayout:newLayout: 代理办法,返回过渡的布局对象
  • 选择性的应用 updateValue:forAnimatedKey: 办法批改布局对象的值,稳固值为 0

应用流水布局 Flow Layout

配置 flow layout 的几个步骤:

  • 生成一个 flow layout 对象并且指定给相应的 collection view
  • 配置 cells 的宽度和高度
  • 设置行距离和单元距离
  • 设置 headers 和 footers 的大小
  • 设置滚动方向

自定义属性

指定 item 的大小

对于所有的 items 能够应用属性 itemSize 来设置大小。如果设定不同的 itemSize,须要实现 collectionView:layout:sizeForItemAtIndexPath: 办法

指定 items 和 lines 之间的距离

留神此处设置的是最小距离,理论距离可能大小此距离。能够通过属性设置 `minimumLineSpacing
minimumInteritemSpacing 或者应用代理办法来实现 collectionView:layout:minimumLineSpacingForSectionAtIndex: collectionView:layout:minimumInteritemSpacingForSectionAtIndex: `

应用内边距设置边缘

应用 Flow Layout 的子类

能够本人生成一个 flowlayout 的子类,而后更改其中的属性办法等实现自定义。

生成自定义的布局 Layout

实现自定义的布局不难,最难的如何在布局中计算 items 地位。

实现 UICollectionViewLayout 子类

继承 UICollectionViewLayout 须要实现的重要的内容:

  • 指定可滚动内容区域的大小 size
  • 提供每个 cell 或 view 的 layout 属性,以便 collection view 能够展现这些 cells

了解 Layout 的布局过程

invalidateLayout 会使以后的 layout 有效,并触发 layout 的更新,会强制 layout 对象从新计算 layout 属性。与 reloadData 不一样,当 data source 中的数据产生扭转,适宜用 reloadData 办法。

在布局过程中,会按顺序调用一下函数,能够在这些办法中计算 item 的地位信息。

  • prepareLayout 办法来执行一些筹备工作,能够进行一些 layout 布局须要的计算等
  • collectionViewContentSize 办法用来返回整个内容的 content size 大小
  • layoutAttributesForElementsInRect: 办法用来返回在指定的矩形区域内的 cells 的属性等信息

prepareLayout是专门用来筹备布局的,在 prepareLayout 办法外面咱们能够当时就计算前面要用到的布局信息并存储起来,避免前面办法屡次计算,进步性能。例如,咱们能够在此办法就计算好每个 cell 的属性、整个 CollectionView 的内容尺寸等等。此办法在布局之前会调用一次,之后只有在调用 invalidateLayoutshouldInvalidateLayoutForBoundsChange: 返回 YES 和 UICollectionView 刷新的时候才会调用。

prepareLayout办法是为确定布局中各 cell 和 view 地位做计算,须要在此办法中算出足够的信息以供后续办法计算内容区域的整体 size,collection view 应用 content size 以正确地配置 scroll view。比方 content size 长宽均超过屏幕的话,程度与竖直方向的滚动都会被 enable。基于以后滚动地位,collection view 会调用 layoutAttributesForElementsInRect:办法以申请特定 rect (有可能是也可能不是可见 rect)中 cell 和 view 的属性。到此,core layout process 曾经完结了。

layout 完结之后,cells 和 views 的属性在你或者 collection view invalidate 布局之前都不会变。调用 invalidateLayout 办法会导致新的一次 layout process 开始,以调用 prepareLayout 办法开始。collection view 能够在滚动的过程中主动 invalidate 布局,用户滚动内容过程中,collection view 调用 layout 的 shouldInvalidateLayoutForBoundsChange: 办法,如果返回值为 YES 则 invalidate 布局。(但须要晓得的是,invalidateLayout 并不会马上触发 layout update process, 而是在下一个 view 更新周期中,collection view 发现 layout 曾经 dirty 才会去更新)

创立布局属性 Layout Attributes

自定义 layout 须要返回一个 UICollectionViewLayoutAttributes 类的对象,这些对象能够在很多不同的办法中创立,但创立工夫能够依据具体情况决定。如果 collection view 不会解决上千个 items 时,则 prepareLayout 创立会比用户滚动过程中用到时在计算更高效,因为创立的属性能够缓存起来。如果计算所有属性并缓存起来所带来的性能耗费比申请时在计算属性的耗费更大,则能够在申请的时候在计算相干属性。

UICollectionViewLayoutAttributes的属性:

  • frame
  • bounds
  • center
  • size
  • transform3D
  • transform
  • alpha
  • zIndex
  • hidden

创立 UICollectionViewLayoutAttributes 类对象时,能够应用一些办法:

  • init(forCellWithIndexPath indexPath: NSIndexPath)
  • init(forSupplementaryViewOfKind:withIndexPath:)
  • init(forDecorationViewOfKind:withIndexPath:)

view 的类型不同,必须应用正确的类办法,因为 collection view 会依据这些信息向 data source 对象申请适当类型的 view,应用谬误的办法在谬误的中央创立谬误的 view。

创立每个属性对象后,要将相应的 view 的相干属性设置上。最根本的要设置 view 的 size 和 position 信息。如果布局中有 view 重叠了,须要配置正确的 zIndex 属性来维持有序的状态。其余属性能够管制 cell 或 view 的外观及可见性。

筹备 Layout

在一个布局周期中,首先会调用 prepareLayout 办法,能够来执行一些筹备工作,能够进行一些 layout 布局须要的计算等,能够存储一些 layout attributes 信息。

给定矩形中的 items 布局属性

layout process 的最初,collection view 会调用 layoutAttributesForElementsInRect: 办法,对于一个大的可滚动内容区域,collection view 可能只会申请以后可见的那局部区域中的所有 items 属性。这个办法反对获取任意 rect 中 items 的信息,因为有可能在插入及删除时做动画成果。

layoutAttributesForElementsInRect: 办法实现须要尊重如下的步骤:

  • 遍历 prepareLayout 办法产生的数据以拜访缓存的属性或创立新的属性
  • 查看每个 item 中的 frame 以确定是否与 layoutAttributesForElementsInRect: 办法中指定的 rect 有重叠局部
  • 对每个重叠的 item,增加一个对应的 UICollectionViewLayoutAttributes 对象到一个数组中
  • 返回布局属性的数组给 collection view

不仅要记住缓存 layout 信息可能带来性能晋升,也要记住一直反复为 cells 创立新 layout 属性的计算代价是非常低廉的,足以影响到 app 的性能。当 collection view 治理的 items 量很大时,采纳在申请时创立 layout 属性的形式是非常正当的。

按需提供布局属性

collection view 会在失常的 layout 过程之外周期性的让你提供单个 items 的 layout 对象。比方为某 item 配置插入和删除对话。通过以下办法提供信息:

  • layoutAttributesForItemAtIndexPath:
  • layoutAttributesForSupplementaryViewOfKind:atIndexPath:
  • layoutAttributesForDecorationViewOfKind:atIndexPath:

layoutAttributesForItemAtIndexPath: 所有自定义的 layout 必须重写的办法。
当返回属性时,不应该更新这些 layout 属性,如果须要扭转 layout 信息,调用 invalidateLayout 在接下来的 layout 周期中更新这些信息。

两种形式设置 collection view 的 layout 为自定义的 layout,

  • 一种形式在 storyboard 文件中,在 Attributes inspector 中设置 Layout 从 Flow 改为 Custom。
  • 另外一种间接代码设置 self.collectionView.collectionViewLayout = [[MyCustomLayout alloc] init];

让你的 Layout 更优异

除了上述必须实现的办法,还有一些个性可能改善自定义的 layout 的用户体验,实现这些属性是可选然而举荐实现的。

通过附加 view 提供内容品质

supplementary views 与 cells 拆散并且有本人的 layout 属性,由 data source 提供,其目标是为 app 次要内容加强信息。UICollectionViewFlowLayout 中应用 supplementary view 来作为 section headers 和 footers,除此之外,能够应用 supplementary views 给每个 cell 提供一个本人的 label,用于显示 cell 的信息。与 cells 一样,supplementary views 也须要重用,所有 supplementary views 须要继承自 UICollectionReusableView 类。

增加 supplementary views 到 layout 的过程如下:

  • 注册 supplementary views 到 layout 对象中,registerClass:forSupplementaryViewOfKind:withReuseIdentifier: 或者 registerNib:forSupplementaryViewOfKind:withReuseIdentifier:
  • 在 data source 中实现 collectionView:viewForSupplementaryElementOfKind:atIndexPath: 办法,因为这些 view 是可重用的,调用 dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath: 来取得 view
  • 像为 cells 创立属性一样,为 supplementary views 创立 layout 属性
  • layoutAttributesForElementsInRect: 办法中返回的属性数组中蕴含 supplementary views 的 layout 属性
  • 如果需要的话,实现 layoutAttributesForSupplementaryViewOfKind:atIndexPath: 办法为特定的 supplementary views 返回属性对象

例子 自定义 collection view 布局

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{NSMutableArray *layoutAttributes = [NSMutableArray array];
    // Cells
    // We call a custom helper method -indexPathsOfItemsInRect: here
    // which computes the index paths of the cells that should be included
    // in rect.
    NSArray *visibleIndexPaths = [self indexPathsOfItemsInRect:rect];
    for (NSIndexPath *indexPath in visibleIndexPaths) {
        UICollectionViewLayoutAttributes *attributes =
        [self layoutAttributesForItemAtIndexPath:indexPath];
        [layoutAttributes addObject:attributes];
    }

    // Supplementary views
    NSArray *dayHeaderViewIndexPaths = [self indexPathsOfDayHeaderViewsInRect:rect];
    for (NSIndexPath *indexPath in dayHeaderViewIndexPaths) {
        UICollectionViewLayoutAttributes *attributes =
        [self layoutAttributesForSupplementaryViewOfKind:@"DayHeaderView"
                                             atIndexPath:indexPath];
        [layoutAttributes addObject:attributes];
    }

    NSArray *hourHeaderViewIndexPaths = [self indexPathsOfHourHeaderViewsInRect:rect];
    for (NSIndexPath *indexPath in hourHeaderViewIndexPaths) {
        UICollectionViewLayoutAttributes *attributes =
        [self layoutAttributesForSupplementaryViewOfKind:@"HourHeaderView"
                                             atIndexPath:indexPath];
        [layoutAttributes addObject:attributes];
    }
    return layoutAttributes;
}

解决 supplementary views 布局属性的过程和 cell 属性过程是一样的,然而不同的是 supplementary views 能够有很多种但只有一种 cell。这是因为 supplementary view 与它们是分来到的,是为了衬托宗旨,所以每个 supplementary view 办法都会指明其类别 kind 以不便正确计算其特有的属性。

在 layout 中增加 Decoration Views

Decoration views 是 Layout UI 特色的无效装点,它仅仅提供一些可视化的内容,与 data source 无关。能够用来自定义背景,在 cells 缝隙之间填充,甚至能够覆盖 cell,齐全由 layout 对象管制。

在 layout 中增加 Decoration view 的步骤:

  • 在 layout 对象中注册自定义的 decoration view,registerClass:forDecorationViewOfKind:registerNib:forDecorationViewOfKind: 办法
  • layout 对象中 layoutAttributesForElementsInRect: 办法中为 decoration view 创立属性
  • 实现 layoutAttributesForDecorationViewOfKind:atIndexPath: 办法并在申请时返回布局属性
  • 选择性的实现 initialLayoutAttributesForAppearingDecorationElementOfKind:atIndexPath: finalLayoutAttributesForDisappearingDecorationElementOfKind:atIndexPath: 来解决呈现和隐没的动画成果

decoration view 与 cell 和 supplementary view 的创立过程不同,仅须要注册 class 或者 nib 即可,最多调用一个 initWithFrame: 办法,不须要额定的配置,留神能够应用 zIndex属性来设置层级构造、任何 decoration view 须要是 UICollectionReusableView 子类,启动了回收机制。

插入和删除动画

插入新的 cell 的时候,collection view 会询问 layout 对象提供一组初始化属性用于动画,完结属性就是默认的地位、属性等。相似的,当删除一个 cell 的时候,collection view 会询问 layout 对象提供一组终值属性用于动画,初始属性默认的 indexPath 地位等。

当插入 item 的时候,layout 对象须要提供正在要被插入的 item 的初始化 layout 信息。在此例中,layout 先将 cell 的初始地位地位到 collection view 的两头,并将 alpha 设为 0,动画期间,此 cell 会慢慢呈现并挪动到右下角。参考代码:

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
   attributes.alpha = 0.0;
 
   CGSize size = [self collectionView].frame.size;
   attributes.center = CGPointMake(size.width / 2.0, size.height / 2.0);
   return attributes;
}

须要留神的是,上述代码在插入 cell 的时候所有的 cell 都会增加此插入的动画,若只想对插入的 item 做插入动画,能够查看 indexPath 是否与传入的prepareForCollectionViewUpdates: 办法的 item 的 indexPath 匹配,并且只有在匹配的时候才进行动画,否则只返回 super 的initialLayoutAttributesForAppearingItemAtIndexPath:. 办法。

override func prepareForCollectionViewUpdates(updateItems: [UICollectionViewUpdateItem]) {super.prepareForCollectionViewUpdates(updateItems)
        insertIndexPath = [NSIndexPath]()
        deleteIndexPath = [NSIndexPath]()
        for update in updateItems {            
            switch update.updateAction {
            case .Insert:
                insertIndexPath.append(update.indexPathAfterUpdate!)
            case .Delete:
                deleteIndexPath.append(update.indexPathBeforeUpdate!)
            default:
                print("error")
            }            
            if update.updateAction == UICollectionUpdateAction.Insert {}}
        
    }

delete 动画与插入相似,须要提供正确的 final layout 属性。

晋升 layout 的滚动体验

当滚动相干的 touch 事件完结后,scrollview 会依据以后的 speed 和加速情况决定最终会停在哪个偏移。一旦 collection view 晓得这个地位后,它就会询问 layout 对象是否批改这个地位,通过调用 targetContentOffset(forProposedContentOffset:withScrollingVelocity:)。因为是在滚动过程中调用此办法,所以自定义 layout 能够扭转滚动的进行地位。

下图展现了调整滚动个性的成果。

如果 collection view 开始于(0,0),且用户向左滑动,collection view 计算出滚动原本会停在如下的地位,这个值是“proposed”content 的 offset 值。自定义 layout 能够扭转这个值,以确保滚动停下的时候,某个 item 正好停留在可见区域的正中间。这个新值会成为新的指标的 content offset,这个值从 targetContentOffsetForProposedContentOffset:withScrollingVelocity: 办法中返回。

例子:

override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {        
        // 计算最终显示的矩形框
        var rect: CGRect = CGRectZero
        rect.origin.y = 0
        rect.origin.x = proposedContentOffset.x
        rect.size = (collectionView!.frame.size)        
        // 依据最终的矩形来取得 super 曾经计算好的属性
        let originArray = super.layoutAttributesForElementsInRect(rect)
        let attributes = NSArray(array: originArray!, copyItems: true) as? [UICollectionViewLayoutAttributes]
        // 计算 collectionView 最中心点的 x 值
        let centerX = proposedContentOffset.x + collectionView!.frame.size.width * 0.5
        // 寄存做小间距
        var minDelta: CGFloat = CGFloat(MAXFLOAT)
        for attrs in attributes! {if abs(minDelta) > abs(attrs.center.x - centerX) {minDelta = attrs.center.x - centerX}
        }
        // 批改原有的偏移量
        return CGPointMake(proposedContentOffset.x + minDelta, proposedContentOffset.y)
        
    }

改良自定义布局的倡议

  • items 数量较少时,数百个或者 items layout 信息变动较小时,能够在 prepareLayout 中创立并缓存 UICollectionViewLayoutAttributes 布局属性对象信息;当 items 数量达到上千个时候,须要掂量缓存和从新计算两种形式的性能差别
  • 禁止继承 UICollectionView
  • 不要在 layoutAttributesForElementsInRect: 办法中调用 UCollectionViewvisibleCells 办法,因为其实这个调用是转化成了向 layout 对象申请visible cells 办法。

Demo

Github 地址:CollectionView Demo

学习办法

如何查问 Apple 官网文档?

  • 苹果 Xcode 帮忙文档浏览指南
  • iOS 开发帮忙文档应用阐明

参考资料

  • 官网文档:Collection View Programming Guide for iOS
退出移动版