• iOS UIKit:CollectionView之设计 (1)


    collection view(UICollectionView对象)使用灵活和可扩展的布局来描述有序的数据项,其一般情况下以网格的形式来展示内容,但并非一定如此。  

    1 基础

          为了将数据展示在屏幕中,Collection View需要搭配其它多种对象,其中有些是用户可选对象,而有些则是必须使用类型。

    1.1 配合对象

          collection views的设计思想与table view的设计思想类似,都是将数据与展示分开,并且也涉及data source和delegate等多种类型,如表 11所示,涉及的每个类,及其负责的功能。

    表 11 The classes and protocols for implementing collection views

    Purpose 

    Classes/Protocols 

    Description

    顶层容器和管理

    UICollectionView

    UICollectionViewController 

    UICollectionView对象定义了可视化区域,该类继承UIScrollView;

    UICollectionViewController对象负责管理collection view对象,其继承UIViewController类。

    内容管理

    UICollectionViewDataSource(protocol)

    UICollectionViewDelegate(protocol)

    Data source对象是collection view最重要的对象,其管理和创建显示的内容。

    Delegate对象提供了用户与collection view对象交换的方式。

    展示

    UICollectionReusableView

    UICollectionViewCell 

    所有在collection view中展示的view对象都必须是UICollectionReusableView实例化对象,这个类提供了一种循环使用的机制。

    UICollectionViewCell对象是一种循环使用的view,其是主要的使用对象。

    布局

    UICollectionViewLayout

     

    UICollectionViewLayoutAttributes

     

    UICollectionViewUpdateItem 

    UICollectionViewLayout对象负责管理cell和view的位置、大小和可视化属性。

    在collection view布局执行区间,布局对象(layout object)创建了布局属性(UICollectionViewLayoutAttributes对象),从而告诉cell的布局信息。

    不管数据项什么时候被插入、删除和移动,布局对象(layout object)都会接收到UICollectionViewUpdateItem对象。用户从来都不需要手动创建该对象。

    流布局

    UICollectionViewFlowLayout

    UICollectionViewDelegateFlowLayout

    UICollectionViewFlowLayout是一种实体布局类,用户使用该类对象来实现网格布局或流式布局。

           如图 11所示展示了collection view相关对象之间的协作关系,collection view对象从data source对象中获得显示的cell对象;layout 对象使用layout attribute对象来管理cell对象的位置,并将这些layout attribute对象发送给collection view对象;最终collection view对象合并layout 信息和cell信息,并在视图中创建可视化内容。

    图 11 Merging content and layout to create the final presentation

    1.2 Reusable Views

          Collection view使用view的循环利用程序来改善性能。当view对象离开屏幕时,则将其移入reuse queue,而不是将其删除;当有新的内容需要被展示在屏幕时,那么可以使用reuse queue中的view对象,只是数据不同而已。为了促进循环使用,所有在collection view中显示的view类都必须继承UICollectionReusableView类。

          Collection view支持如下三种可循环使用类型,每种类型都有明确的用处和用法:

           1) Cells

              该类型为collection view的主要显示内容,其工作是描述简单项的内容。每个cell对象都是UICollectionViewCell类的实例化对象。

           2) Supplementary views

             该类型为展示collection view的section信息。与cell对象类似,supplementary view对象也是数据驱动类型,不同的是supplementary view不是强制的,其使用和布置都是由layout object管理。

           3) Decoration views

             该类型为一种可视化装饰类型,而且它不是由data source管理的数据驱动类型,而是完全由layout object管理。

    1.3 Layout Object

          layout object完全负责可视化组件的位置和样式,虽然data source提供显示的view对象,但是layout object负责管理view对象的位置、尺寸和显示外观。这种分开独立的责任使得在不修改view对象的情况下,可动态改变view对象的布局。

          注意不要将collection view的布局管理与app的子view的布局管理相混淆。collection view的布局管理不需要直接管理这些view对象,相反,layout object创建一些布局信息来描述cells、supplementary views,、和 decoration views对象的位置、尺寸和可视化外观,使得collection view应用这些信息来构建这些view对象的布局。

    2 Data Source 与Delegate

         与table view类似,collection view也需要data source对象和delegate对象。

    • Data source(必选)其是collection view展示的对象提供者。
    • Delegate(可选)其提供collection view与用户(开发工程师)进行信息交换的方式。

    2.1 管理内容

          Data source对象负责管理collection view的内容,其中data source对象所属的类必须遵守UICollectionViewDataSource协议。Data source必须向collection view对象提供如下的信息:

    • collection view包含多少项section;
    • collection view每项section又包含了多少个item(cell);
    • 每项section和每个item展示什么内容。

          Collection view使用多层深度NSIndexPath对象来定位数据项。对于item对象,NSIndexPath对象仅包含两层深度的内容,即一个section数和一个item数;但对于supplementary 和decoration view对象,NSIndexPath对象则可能包含更多层的内容,主要依赖app是如何布局和设计。

          NSIndexPath对象是由layout object创建和提供,即section和item的可视化信息是由layout object决定,不同的布局信息展示的section和item信息是完全不同的。如图 21所示,flow layout object展示的section对象是在垂直方向上连续布局,而custom layout提供的section则是非连续的布局安排。

    图 21 Sections arranged according to the arrangement of layout objects

    2.1.1 数据模型

          Apple官方建议采用二维的section和item来组织底层的数据模型,采用这种方式来组织能够更快的访问数据。如图 22所示,底层数组中包含多个子数组,每个数组描述一个section对象的内容,而每个section数组又包含多个item元素。

    图 22 Arranging data objects using nested arrays

    2.1.2 模型数量

         collection view会不断向data source询问有多少项section和多少个item,当如下事件发生时,collection view对象就会询问data source这些信息:

         a) collection view第一次被展示时;

         b) 修改collection view对象的data source对象时;

         c) 用户精确的调用collection view对象的reloadData方法时;

         d) collection view delegate对象执行performBatchUpdates:completion:方法时,或者是其执行的move、 insert或 delete 方法。

         为了回答collection view这些信息,所以data source对象需要实现UICollectionViewdataSource协议两个方法:

             1) numberOfSectionsInCollectionView:方法

            该方法返回collection view中有多少项section对象。该方法为可选类型,若未实现该方法,则默认返回为1。

             2) collectionView:numberOfItemsInSection:方法

            该方法返回每项section有多少个item,并且该方法为必选类型

    如下所示的实现,_data为预先定义的二维数组:

    1 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView {
    2     // _data is a class member variable that contains one array per section.
    3     return [_data count];
    4 }
    5 
    6 - (NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section {
    7 NSArray* sectionArray = [_data objectAtIndex:section];
    8 return [sectionArray count];
    9  }  

    2.2 Cells和Supplementary Views配置

           Data source对象的另一个重要任务是提供collection view具体显示的内容:cell和supplementary view对象。Collection view不会遍历这些内容,只是向layout object查询layout attribute信息,然后将布局信息应用于显示内容。为了向collection view提供这些cell和supplementary view对象,需要用户(开发工程师)实现如下内容:

            1) 在storyboard文件中,必须嵌入cell或view模版;同时可以选择为每个cell或supplementary view注册(关联)一个controller类。

            2) 在data source中,配置reuse queue并配置合适的cell和supplementary view。

          为了尽可能高效地使用cell和supplementary view对象,每个collection view都维护一个内置的cell和supplementary view队列(reuse queue)。即当需要显示这些cell和view对象时,不需要进行创建,从而节省的时间和硬件性能。

    2.2.1 注册

          为了配置和注册cell和supplementary view对象,有两种方式:program和storyboard。

    1) storyboard方式

          这种方式使用非常简单,只需从库中拖拽item到collection view,并配置相关的属性,其实就是创建collection view与cell(或supplementary view)直接关系。

    • 若是cell对象:从库中拖拽一个collection view cell控件到collection view视图中,然后创建定制的class,并将此class关联到cell控件中,同时为cell控件设置reusable view identifier值。
    • 若是supplementary view对象:从库中拖拽一个Collection Reusable View控件到collection view视图中,然后创建定制的class,并将此class关联到该控件中,同时为该控件设置reusable view identifier值。

    2) program方式

          这种方式是使用UICollectionView对象的不同方法来注册cell和supplementary view对象。

    • 若是cell对象

    - (void)registerClass:(Class)cellClassforCellWithReuseIdentifier:(NSString *)identifier

    - (void)registerNib:(UINib *)nibforCellWithReuseIdentifier:(NSString *)identifier

    参数语义:

    cellClass:为cell控件关联的类class属性;

    identifier:为在以后要重复使用的标识符;

    nib:为包含cell对象的nib对象。

    • 若是supplementary view对象

    - (void)registerClass:(Class)viewClassforSupplementaryViewOfKind:(NSString *)elementKind  

                                                                    withReuseIdentifier:(NSString *)identifier

    - (void)registerNib:(UINib *)nibforSupplementaryViewOfKind:(NSString *)kindwithReuseIdentifier:(NSString *)identifier

    参数语义:

    viewClass:为view控件关联的类class属性;

    elementKind:layout object定义的标识符;

    identifier:为在以后要重复使用的标识符;

    nib:为包含view对象的nib对象。

    kind:

     

           注册cell和supplementary view对象,必须在进行出队之前进行;并且一旦注册之后,即可重复使用cell和supplementary view对象,而无需在重复注册。Apple不推荐在出队一个或多个对象之后,再修改注册信息。

    2.2.2 出队和配置

           当Collection view需要显示内容时,它就会向Data source 对象请求cell和supplementary view对象。这里的请求其实是调用UICollectionViewDataSource协议的两个方法:

    - (UICollectionViewCell  *)collectionView:(UICollectionView *)collectionView
                  
                       cellForItemAtIndexPath:(NSIndexPath *)indexPath

    -(__kindof UICollectionReusableView*)dequeueReusableSupplementaryViewOfKind:(NSString*)elementKind
 
                                                            withReuseIdentifier:(NSString*)identifier
                                                                   forIndexPath:(NSIndexPath *)indexPath

          其中第一个方法是返回cell对象,用户必须实现该方法;而第一个方法返回supplementary view对象,其是可选方法,具体依赖布局的类型。但两个方法内部都可以按如下操作:

          1) 从如下两个方法出队cell对象或supplementary view对象:

    • dequeueReusableCellWithReuseIdentifier:forIndexPath:
    • dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath:

          2) 使用index path对象配置数据信息;

          3) 返回cell或view对象。

          Reuse queue会自动从storyboard或nib中创建这些cell和supplementary view对象,并且调用其initWithFrame:方法进行初始化,所以用户可以在关联的class中实现该方法。在创建显示内容后,即可对其进行配置,如下所示:

    1 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
    2                   cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    3     MyCustomCell* newCell = [self.collectionView dequeueReusableCellWithReuseIdentifier:MyCellID
    4                                                                  forIndexPath:indexPath];
    5     
    6     newCell.cellLabel.text = [NSString stringWithFormat:@"Section:%d, Item:%d", indexPath.section, indexPath.item];
    7     return newCell;
    8 }

    注意:

        若返回的cell和supplementary view对象为nil,或者是其它原因不能显示,那么将导致一个assert错误并中断app。

    2.3 section和item编辑

           为了插入、删除或移动section对象和item对象,需要按如下步骤操作:

    1. 更新data source的数据模型;
    2. 调用UICollectionView对象的插入、删除或移动的合适方法。

    2.3.1 简单编辑

          与UITableView类似,UICollectionView也提供了一些方法来编辑单一的某一项section,或一个item。如下所示当用户点击collection view的一个item时,就将其删除:

    1 - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
    2 {
    3     [_array[indexPath.section] removeObjectAtIndex:indexPath.item];
    4     NSArray * indexs = [NSArray arrayWithObjects:[NSIndexPath indexPathForItem:indexPath.item inSection:indexPath.section],
    5                                                          nil];
    6     [collectionView deleteItemsAtIndexPaths:indexs];
    7 }

         除了上述的删除操作外,还有插入和交换等操作方法,具体内容可参考UICollectionViewDelegate。

    2.3.2 批量编辑

         与UITableView类的批量操作不同,UICollectionView类提供performBatchUpdates:completion:方法来完成(而不是放在两个方法之间),将UICollectionView的编辑方法都放在如下方法的block中。

    - (void)performBatchUpdates:(void (^)(void))updates  completion:(void (^)(BOOL finished))completion

    参数语义:

    updates:为更新的block,对collection view的section或item编辑都放在该block中;

    completion:为完成后的操作,可以为nil。

    如下所示,当用户点击collection view的某一项时,将其删除并插入一项新内容,即替换新项:

     1 - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
     2 {
     3     [_array[indexPath.section] removeObjectAtIndex:indexPath.item];
     4     [_array[indexPath.section] insertObject:@"hlw" atIndex:indexPath.item];
     5     NSArray * indexs = [NSArray arrayWithObjects:[NSIndexPath indexPathForItem:indexPath.item inSection:indexPath.section],
     6                                                          nil];
     7     [collectionView performBatchUpdates:^{
     8         [collectionView deleteItemsAtIndexPaths:indexs];
     9         [collectionView insertItemsAtIndexPaths:indexs];
    10     } completion:nil];
    11 }

    2.4 Selection与Highlight

    2.4.1 状态

    1) 基本概念

        collection view支持对cell进行多种操作:单选、多选和不可选。Collection view会自动探测到对cell的操作。可以将collection view cell有两种特殊状态:

    • selection该状态是cell的一种长期的状态,是指cell被选择过;
    • highlight该状态是cell的一种短暂状态,是指cell目前被强调。

    2) 背景视图

         Collection view会修改cell的属性来指明其cell是selection状态或是hightlight状态,并且UICollectionViewCell对象有一个selectedBackgroundView属性(为UIView类型),当cell对象为selection或hightlight状态时,那么cell将显示selectedBackgroundView属性的背景视图。

     1 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
     2 {
     3     contentCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
     4     cell.myLabel.text =[NSString stringWithFormat:@"S%ld: %@",indexPath.section,_array[indexPath.section][indexPath.item]];
     5     
     6     UIView* backgroundView = [[UIView alloc] initWithFrame:cell.bounds];
     7     backgroundView.backgroundColor = [UIColor redColor];
     8     cell.backgroundView = backgroundView;  //一般情况下显示的背景
     9     
    10     UIView* selectedBGView = [[UIView alloc] initWithFrame:cell.bounds];
    11     selectedBGView.backgroundColor = [UIColor whiteColor];
    12     cell.selectedBackgroundView = selectedBGView;  //当被选中过,或处于强调,则显示该背景
    13     return cell;
    14 }

    影 21 效果图

    3) 状态区别

        selection和hightlight两种状态之间存在细微的区别,两者分别由UICollectionViewCell类的selected性以及highlighted属性来标识。并且当用户触碰cell对象时,其状态的变化也不一样,如图 23所示,当点击cell过程中selected和highlighted的变化:

    • 当手指按下cell对象时,hightlighted为YES,而selected为NO;
    • 当手指抬起时,hightlighted为NO,而selected为NO;
    • 当最后结束点击时,hightlighted为NO,而selected为YES。

    图 23 Tracking touches in a cell

    2.4.2 响应方法

          根据cell的两种状态变化,collection view delegate提供如下的方法来响应cell状态的变化,用户可以根据需要实现这些方法或之一:

    collectionView:shouldSelectItemAtIndexPath:

    collectionView:shouldDeselectItemAtIndexPath:

    collectionView:didSelectItemAtIndexPath:

    collectionView:didDeselectItemAtIndexPath:

    collectionView:shouldHighlightItemAtIndexPath:

    collectionView:didHighlightItemAtIndexPath:

    collectionView:didUnhighlightItemAtIndexPath:

    2.5 Edit Menu

          当长按cell对象时,会出现一个上下文菜单,有3个菜单项:cut、copy和paste。但要显示这个上下文菜单,必须实现UICollectionViewDelegate的3个方法:

             1) collectionView:shouldShowMenuForItemAtIndexPath:方法

             该方法必须返回YES,指明要上下文菜单。

             2) collectionView:canPerformAction:forItemAtIndexPath:withSender:方法

             该方法也必须返回YES,然后立即会出现一个有3项的菜单。

             3) collectionView:performAction:forItemAtIndexPath:withSender:方法

             当用户选择3个菜单项之一,则会执行该方法。

    如下所示,当用户点击某一项菜单项,则输出相应的名字:

     1 -(BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath
     2 {
     3     return YES;
     4 }
     5 -(BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
     6 {
     7     return YES;
     8 }
     9 
    10 -(void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
    11 {
    12     NSLog(@"%@",NSStringFromSelector(action));
    13 }

    影 22 效果图

    5 参考文献

        [1] Collection View Programming Guide for IOS.

     

  • 相关阅读:
    括号序列的dp问题模型
    粉刷匠
    木棍加工
    物流运输
    最短路图
    DP基础(线性DP)总结
    离散化
    树链剖分
    NOIP2016 “西湖边超萌小松鼠” 模拟赛
    NOI导刊 2009 提高二
  • 原文地址:https://www.cnblogs.com/huliangwen/p/5471110.html
Copyright © 2020-2023  润新知