使用xib文件创建集合类单元格

时间:2021-01-23 17:02:37

UICollectionView是一种新的数据展示方式,简单来说可以把它理解成多列的UITableView。如果你用过iBooks的话,可能你还对书架布局有一定印象,一个虚拟书架上放着你下载和购买的各类图书,整齐排列。其实这就是一个UICollectionView的表现形式,或者iPad的iOS6中的原生时钟应用中的各个时钟,也是UICollectionView的最简单的一个布局。

集合视图UICollectionView介绍

集合视图UICollectionView和表视图UITableView很相似,可根据layout属性设置,显示单元格集合内容。UICollectionViewDataSource类作为集合视图的数据源,向集合视图提供数据。集合视图依赖于委托(Delegate)中定义的方法对用户交互进行响应。 
构成集合视图的三个要素,分别为:单元格(UICollectionViewCell)、补充视图(Supplementary Views-显示额外的元数据信息)和装饰视图(Decoration Views)。

使用xib文件创建集合类单元格

不管一个UICollectionView的布局如何变化,这三个部件都是存在的。 
为什么要使用集合视图呢?

  • 可以高度定制内容的显示;
  • 管理数据最佳的做法;
  • 可以高效处理大量数据;

集合视图单元格UICollectionViewCell

类似于表视图单元格UITableViewCell,它有一个indexPath属性定义它属于哪一个行和节点,以及其他属性定义可视化显示。有点和UITableViewCell不一样的是,UICollectionViewCell没有任何预定义的类型,我们必须手工设置单元格。

集合视图布局UICollectionViewLayout 
这个类控制单元格如何布局,如定位、透明度和层级(z-index)等等。UICollectionViewFlowLayout是UICollectionViewLayout的一个预定义的子类,用来设置按行显示的流布局(flow layout)。不过,我们可以进一步重载其属性或开发子类来定制化。

集合视图数据源UICollectionViewDataSource 
和UITableViewDataSource很像,UICollectionViewDataSource负责提供单元格给集合视图。通过UICollectionViewDataSource协议来定义,该协议提供了一些必须的方法,以及大量的可选方法。

集合视图委托UICollectionViewDelegate 
和表视图委托UITableViewDelegate很像,负责处理用户交互,通过UICollectionViewDelegate协议来定义。

创建一个简单的集合视图应用程序

我们先创建一个简单的应用集合视图的应用程序,具体看看效果。 
使用Xcode的Single View Application模板,创建一个项目,项目名称为SimpleCollectionView,类前缀为Simple。针对这个项目,我们不选择Use Storyboards,但选择Use Automatic Reference Counting复选框。

为了创建集合视图,需要让类遵守集合视图的相关协议。打开SimpleViewController.h文件,添加UICollectionViewDataSource和 UICollectionViewDelegate协议。 

#import <UIKit/UIKit.h>
@interface SimpleViewController : UIViewController<UICollectionViewDataSource, UICollectionViewDelegate>
@end

接着打开SimpleViewController.xib文件,从对象库中拖拉UICollectionView对象到视图中,让集合视图填充整个视图。 
然后建立UICollectionView对象到视图控制器中的输出口,如下所示: 
@property (strong, nonatomic) IBOutlet UICollectionView *collectionView;

切换回SimpleViewController.xib文件,按住Control键,点击视图中的UICollectionView对象,拖拉到File’s Owner图标上,从弹出菜单中,选择datasource。重复相同的操作,连接到delegate输出口。

使用xib文件创建集合类单元格

这样,完成了UICollectionView对象到datasource和delegate输出口的连接。接下来,我们需要为UICollectionView准备数据。 
为了让应用程序简单一些,我们在SimpleViewController.m实现文件的附加目录(Continuation Category)添加一些私有属性,如下所示: 
#import "SimpleViewController.h"
@interface SimpleViewController ()
@property (nonatomic, strong) NSArray *dataArray;
@end

dataArray数组将存放UICollectionView所需要的数据。我们在viewDidLoad方法初始化2个数组,分别为2个section,每个包括50个NSString数据项。 
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSMutableArray *firstSection = [[NSMutableArray alloc] init];
NSMutableArray *secondSection = [[NSMutableArray alloc] init];
for (int i=0; i<50; i++){
[firstSection addObject:[NSString stringWithFormat:@"单元格 %d", i]];
[secondSection addObject:[NSString stringWithFormat:@"数据项 %d", i]];
}
self.dataArray = [[NSArray alloc] initWithObjects:firstSection, secondSection, nil];
}

创建好数据之后,接下来我们需要告诉集合视图有几部分(section),这表示我们需要实现numberOfSectionsInCollectionView:方法。 
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return [self.dataArray count];
}

在集合视图知道有几个部分之后,我们还需要告诉集合视图:每一部分(section)所包含的数据项。因此,我们需要实现collectionView: numberOfItemsInSection:方法。 
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
NSMutableArray *sectionArray = [self.dataArray objectAtIndex:section];
return [sectionArray count];
}

现在,集合视图已经知道有几个部分,并且每一部分的数据项。接下来,我们创建用于显示的单元格。

创建集合视图单元格UICollectionViewCell

表视图UITableView提供一些标准的单元格布局,但对于UICollectionView,我们需要创建自己的单元格。和表视图创建单元格一样简单,我们可以在一个独立的xib文件,或者UICollectionViewCell子类创建单元格。下面,我们会分别演示这两种方法。

使用xib文件创建集合视图单元格

创建一个新的视图文件,选择File > New > File… 菜单项,从iOS的User Interface节点下,选择View模板,新建文件命名为NibCell。

使用xib文件创建集合类单元格

Xcode将自动打开这个文件,我们删除默认的视图(view),然后从对象库中拖拉Collection View Cell对象到画布中。

选择上述单元格视图,在Size inspector面板窗口,调整尺寸为100×100。

然后在Attributes inspector面板窗口,设置背景色为你喜欢的颜色。这里,我们设置背景色为黄色。

我们在单元格中添加一个Label标签。选中该标签,在Attributes inspector 面板窗口中,设置其Tag 属性为10。后面的代码我们用到这一属性值。

使用xib文件创建集合类单元格

创建好xib文件后,现在我们需要关联到集合视图了。同时,我们也应用UICollectionViewLayout布局到集合视图中。回到视图控制器实现代码文件中,更新viewDidLoad方法,添加如下代码: 
UINib *cellNib = [UINib nibWithNibName:@"NibCell" bundle:nil];
[self.collectionView registerNib:cellNib forCellWithReuseIdentifier:@"simpleCell"];

上述代码获取前面创建的xib文件,并注册到集合视图中,设置可重用识别符为simpleCell,可用来出队列并实例化一个新的可供使用的单元格。

一个集合视图如果没有布局,是没有用途的。因此,我们需要创建布局。添加如下代码到viewDidLoad方法的底部。 
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setItemSize:CGSizeMake(100, 100)];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
flowLayout.sectionInset = UIEdgeInsetsMake(0, 2, 0, 0);

 

[self.collectionView setCollectionViewLayout:flowLayout];

上述代码首先创建一个UICollectionViewFlowLayout对象实例,该对象是iOS SDK内置的可供使用的流布局。该对象有很多属性可供设置,这里我们设置了3个属性:(1) item的大小,和前面我们创建的UICollectionViewCell一样大小;(2) 滚动方法;(3) 设置集合视图section之间的间隔。最后,我们将配置好的布局添加到集合视图中。

现在,我们准备好实现方法,返回集合视图所需要的单元格了,这个需要实现的就是collectionView:cellForItemAtIndexPath:方法,和tableView:cellForRowAtIndexPath:方法非常相似。在视图控制器实现文件中,添加如下方法代码: 
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
NSMutableArray *data = [self.dataArray objectAtIndex:indexPath.section];
NSString *cellData = [data objectAtIndex:indexPath.row];
static NSString *cellIdentifier = @"simpleCell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
UILabel *titleLabel = (UILabel *)[cell viewWithTag:10];
titleLabel.text = cellData;
return cell;
}
 
上述代码,首先从dataArray数据源获取指定section的数据源,从单元格的字符串从data数组中获取指定行(row)数据。接着,创建cellIdentifier引用,要求集合视图取出一个可重用的单元格,其带有cellIdentifier标识符。如果在集合视图的缓存中存在一个可重用的单元格,将返回给我们使用,否则集合视图会在幕后为我们创建一个新的单元格对象。我们不必知道幕后的处理过程,只需了解dequeueReusableCellWithReuseIdentifier:forIndexPath:方法总是会返回一个UICollectionViewCell对象。 
我们之前在UICollectionViewCell中添加一个UILabel标签,并设置tag属性为10。这意味着我们可以检查单元格,并获取标签的引用,并转换为UILabel变量,这样才可以进一步访问UILabel属性。 
一旦UILabel属性可以访问,我们设置其text属性值,并最终返回单元格,在集合视图中显示。

运行SimpleCollectionView应用程序

现在代码编写好了,我们运行SimpleCollectionView应用程序,运行效果应该如下所示。

使用xib文件创建集合类单元格

如上图所示,集合视图中显示了2个section的数据。