(apple官方文档译文)深入理解表视图的单元格(cells)- A Closer Look at Table View Cells

深入理解表视图的单元格(cells)

原文连接

<这部分内容有点多,但很重要 ,有不对的欢迎指正! 转载需注明>


一个表视图通过cell对象来绘制可见的单元格,只要这些单元格是可见的就会将他们放入缓存中。Cell继承自UITableViewCell类。如果视图控制器实现了UITabelViewDataSource协议,通过实现tabelView:cellForRowAtIndexPath:方法,数据源会为表视图提供所有cell对象,即单元格。

在本章节,你将会学到:

  • cells的基本特征
  • 如何使用UITableViewCell类的默认属性来创建cell里的内容
  • 如何自定义一个UITabelViewCell对象
cell对象的基本特征

一个cell对象内部可以划分不同的区域,这些区域表视形式随着表视图模式的不同而发生变化。通常情况下,大部分的表视图都会包含这些内容:文本、图片和其他一些特殊的标示符。图5-1展示了一个cell中最主要的部分。


图5-1表视图中cell对象的区域分布


右侧比较小的区域是为特殊标示符预留的:扩展指示器、细节展示按钮和一些控制视图对象如滑块、开关等,也可以自定义视图。


当你的表视图进入编辑模式的时候,所有的cell对象的控制视图对象(如果你配置了这样的操作装置)会出现在它的左侧,图5-2展示了它的区域变化

图5-2进入编辑状态下cell各部分的状态

(apple官方文档译文)深入理解表视图的单元格(cells)- A Closer Look at Table View Cells_第1张图片

编辑操作控件可以是删除控件(带有红色减号的圆形)或者是插入控件(带有绿色加号的圆形)。Cell的内容为了给编辑操作控件让出一定位置,所以要向右侧移动。如果cell对象配置了排列功能,排列控件出现在cell的右侧,紧靠着cell的扩展视图。排列控件是一组平行线,用来重新排列表视图中的单元格(cell),用户按下排列控件,然后就可以拖拽单元格(cell)。


如果一个cell对象可以重用(非常常见的情况)你可以在storyboard中为它设置一个重用标示符(通常是一个字符串)。在程序执行中,表视图以列队的形式将它们在内部存储下来。当表视图向数据源请求一个cell对象去显示的时候,数据源通过向表视图发送一个dequeueReusableCellWithIdentifier:消息,并传递一个重用标示符,来使用内部列队中的cell对象。数据源会在返回一个cell对象之前设置cell的内容和一些特殊的属性。这个被重用的cell对象可以使性能大大提高,因为它可以避免重复创建。

 

当列队中存在多个cell对象,并且每个cell对象都又自己的重用标示符,这时你可以将不同类型的cell对象构建成一个表视图。例如,可以将其中一些cell对象设置为预定义的UITableViewCell,它们的内容全是基于图片和文字,而另外一些cell对象可以设置为自定义的UITableViewCell子类,它们的内容全是基于一些特殊的格式。

 

你有三种途径为表视图创建cell对象。你可以使用一系列现成的cell类型,或者在这些现成的cell对象中添加子视图(可以在Interface Builder中完成),你还可以创建一个继承自UITableViewCell子类来创建cell对象。

 

使用cell对象的预定义类型

 

当直接使用UITableViewCell类时,你可以直接使用一系列现成的cell类型。“StandardStyles for Table View Cells”描述了这些标准的cell类型,并且展示了一些图片例子。这些cell类型是一些枚举类型的常量,在UITableViewCell.h文件中声明了这些常量:

typedef enum{
		
	UITableViewCellStyleDefault,
	UITableViewCellStyleValue1,
	UITableViewCellStyleValue2,
	UITableViewCellStyleSubtitle
} UITableViewCellStyle;

这些cell对象中一般包括两种类型的内容:一个或多个字符串文本,有时候还包括一张图片。图5-3展示了文字和图片的大致区域。图片、文字按照从左到右的顺序排列。

 

图5-3 系统默认的UITabelViewCell类型的cell对象

(apple官方文档译文)深入理解表视图的单元格(cells)- A Closer Look at Table View Cells_第2张图片

UITableViewCell类为它的实例cell对象内容定义了三个属性:

  • textLabel – 标题标签(一个UILable类型的对象)
  • detailTextLabel – 副标题标签,如果有一些额外的细节需要展示(一个UILable类型的对象)
  • imageView – 图像视图(一个UIImageView类型的对象)

由于前两个属性都是标签类型的,所以你可以依据UILabel类属性来设置字体、对齐方式、字体颜色、换行等(也可以设置当cell对象高亮时的字体颜色)。至于imageView属性,你可以设置一个可替换的图片,比如当你的图片视图高亮时要呈现的图片,这些功能UIImageView类都有提供。

 

图5-4的例子就是使用UITableViewCell类绘制的单元格,它使用UITableViewCellStyleSubtitle这个样式属性,它的子视图包含了一张图片、主标题、副标题。


图5-4 一个带有图片和文字的表视图

(apple官方文档译文)深入理解表视图的单元格(cells)- A Closer Look at Table View Cells_第3张图片


列表5-1通过执行tableView:cellForRowAtIndexPath:方法创建像图5-4那样的表视图。首先,数据源应该向表视图发送dequeueReusableCellWithIdentifier:方法,并传递重用标示符。如果在storyboard中已经创建了一个单元格,表视图返回重用这个cell对象。然后设置这个cell对象包含的内容:文本和图像。


列表5-1配置一个带有文本和图像的UITableViewCell对象

- (UITableViewCell *)tableView(UITableView *)tableView cellForRowAtIndexPath(NSIndexPath *)indexPath
{

UITableViewCell *cell = [tabelView dequeueReusableCellWithIdentifier:@”MyIdentifier”];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITabelViewStyleSubtitle reuseIdentifier:@”MyIdentifier”];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
NSDictionary *item = (NSDictionary *)[self.content objectAtIndex:indexPath.row];
cell.textLabel.text = [item objectForKey:@”mainTitleKey”];
cell.detailTextLabel.text = [item objectForKey:@”secondaryTitleKey”];
NSString *path = [[NSBundle mainBundle] pathForResource:[item objectForKey:@”imageKey”] ofType:@”png”];
UIImage *theImage = [UIImage imageWithContentOfFile:path];
cell.imageView.image = theImage;

return cell;
}

表视图的数据源执行tableView:cellForRowAtIndexPath:方法的时候,即使重用cell对象,也会重新设置cell对象的所有内容。


当你创建一个UITableViewCell对象的时候,你可以设置一些其他属性(但不是必须的),如下:

  •   selectionStyle – cell单元格被选中时的样式
  •   accessoryType和accessoryView – 允许你在正常状态下为cell单元格设置一个标准的扩展视图控件(如扩展指示器、细节展示按钮),或者自定义一个扩展视图,你可以创建一个自定义的UIView视图对象,例如滑块、开关等。
  •   editingAccessoryType和editingAccessoryView -允许你在编辑状态下为cell单元格设置一个标准的扩展视图控件(如扩展指示器、细节展示按钮),或者自定义一个扩展视图,你可以创建一个自定义的UIView视图对象,例如滑块、开关等。
  •   showReorderControl – 指定当表视图处于编辑状态的时候是否显示重新排列控件。通过只读属性editingStyle来指定此控件是否显示。表视图的代理会在tableView:editingStyleForRowAtIndexPath:方法中返回editingStyle属性的值。
  •   backgroundView和selectedBackgroundView – 设置背景视图(当cell单元格选中和没选中时),它位于所有cell子视图的下方。
  •   indentationLevel和indentationWidth – 指定cell单元格的缩进级别和每个级别的宽度
由于表视图的cell单元格继承自UIView,所以你可以通过设置它父类的属性值来改变它的呈现形式和一些行为。例如,你可以通过backgroudColor属性改变cell单元格的背景颜色。列表5-2向你展示了如何使用代理方法tableView:willDisplayCell:forRowAtIndexPath:来替换原有cell单元格的背景颜色。


列表5-2替换cell单元格的背景颜色

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row%2 == 0) {
        UIColor *altCellColor = [UIColor colorWithWhite:0.7 alpha:0.1];
        cell.backgroundColor = altCellColor;
    }
}

列表5-2举例说明了表视图API中一个重要的知识点。表视图在绘制单元格之前向它的代理发送了tableView:willDisplayCell:forRowAtIndexPath:消息,如果代理选择执行这个方法,它就会在单元格呈现之前的最后时刻修改cell单元格。通过次方法,代理只能修改表视图的早些时候设置的基本属性,比如选择状态和背景颜色,但不能修改单元格的内容。


自定义cell单元格


四种预定义类型的UITableViewCell对象已经能够满足大部分应用。这些现成的cell单元格对象包含一到两个文本、一张图像和其他扩展控件。可以定义文本的字体、颜色、和其他特征,图像只支持点选和正常状态两种模式。

 

Cell单元格的灵活、实用,所以预定义类型不能满足所有应用的需求。比如标签文本必须放在单元格内的固定位置,图像必须出现在单元格的左侧。如果你想让单元格拥有不同的组件,并且将它们置于不同的位置,或者你想让单元格拥有不通的行为特征,你有两种途径:

  •    为cell单元格添加子视图
  •   自定义一个UITableViewCell子类

下面将要讨论这两种途径。

通过storyboard加载表视图单元格

在storyboard中,cell单元格可分为动态和静态两种类型。动态单元格:表视图拥有数量较多而且不受限制的cell单元格。静态单元格:表视图中的单元格数量在程序编译阶段就已经能够确定。专门用作详细内容展示的表视图可以采用静态单元格。

 

你可以直接在表视图对象里设计动态或静态单元格的内容。图5-5展示了storyboard中主表视图和详情展示表视图。例子中,主表视图包含了动态单元格,而详情展示表视图包含静态单元格。

 

图5-5 一个storyboard中的表视图单元格

(apple官方文档译文)深入理解表视图的单元格(cells)- A Closer Look at Table View Cells_第4张图片

下面演示如何往自定义cell单元格里加载数据。

 

创建动态内容单元格的技巧

 

在这小节中你将在storyboard中创建一个自定义cell单元格。在程序执行时,数据源会将这些cell单元格排列,一切准备就绪后,便将它们绘制在表视图中。见图5-6

 

图5-6将自定义的cell单元格绘制到表视图当中

(apple官方文档译文)深入理解表视图的单元格(cells)- A Closer Look at Table View Cells_第5张图片


数据源通过两种方式存取cell单元格的子视图。第一种是通过设置UIView类的tag属性存取,另一种是通过outlet输出值存取。尽管使用tag值需要将它与代码里向关联起来,但是还是比较方便的。使用outlet输出值存取需要做稍多工作,因为你需要自定义一个UITableViewCell子类。下面是两种方式的详细描述。


第一种方式:

创建一个项目工程,使用storyboard来为表视图加载自定义cell单元格

  1. 使用Master-DesterApplication模版创建一个工程项目,勾选use storyboard选项。
  2. 在storyboard画布上选择主视图控制器。
  3. 在Identityinspector面板中,确定Class选项值被设定为自定义的MasterViewController类。
  4. 选择主视图控制器里的表视图。
  5. 在Attributesinspector面板中,确定content选项的下拉列表值被设定为Dynamic Prototypes。
  6. 选择单元格模版。
  7. 在Attributesinspector面板中,在style属性下拉列表中选择Custom。
  8. 在Identifier文本输入框中输入一个重用标示符。
    这里的重用标示符与你给表视图发送dequeueReusableCellWithIdentifier:消息时使用的是同一个重用标示符。见列表5-3
  9. 将Accessory属性的下拉列表选择为Disclosure Indicator
  10. 从library选项卡中拖拽一些控件到单元格。
    例如:拖拽两个lable标签,并将它们放置于单元格的末尾(为扩展视图预留下空间)。
  11. 选择你拖入的控件,设置它的属性、尺寸、自动调整尺寸特征。
    还有一个很重要的步骤,就是在属性面板中为每个控件设置相应的tag值,值为正整数。
现在通过代码编写来获得表视图的数据。(这个例子中,你只需要获得cell单元格的数量。)通过实现数据源方法tableView:cellForRowAtIndexPath:根据模版单元格来创建一个新的cell单元格,并为它添加数据,类似于列表5-3这样。

列表5-3通过使用tag值为单元格添加数据

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyIdentifier"];
 
    UILabel *label;
 
    label = (UILabel *)[cell viewWithTag:1];
    label.text = [NSString stringWithFormat:@"%d", indexPath.row];
 
    label = (UILabel *)[cell viewWithTag:2];
    label.text = [NSString stringWithFormat:@"%d", NUMBER_OF_ROWS - indexPath.row];
 
    return cell;
}

在这个代码列表中有一些方面需要注意:

  • 你为单元格模版设置的重用标示符与通过tableView:cellForRowAtIndexPath:方法传递给表视图的重用标示符是一样的。
  • 因为单元格模版是在storyboard中创建的,tableView:cellForRowAtIndexPath:方法会返回一个有效的cell单元格,你不需要检查它是否是空值,也不需要手动创建一个新的cell单元格。
  • 通过向cell单元格发送viewWithTag:消息,并传递相关控件的正整数tag值来获取对应的空间视图,上述代码中使用这种方法为lable标签赋值。

如果你不太倾向于使用tag属性,你可以使用另一种方法来为cell单元格设置内容。自定义一个UITableViewCell子类,并为你要设置的对象声明outlet属性。

在storyboard中,将单元格里的对象与自定义类中的outlet属性关联起来(也就是连线)。

为自定义cell单元格内容使用outlets:

  1. 为你的项目添加一个名称为MyTableViewCell的object-c类。
  2. 在MyTableViewCell.h中加入以下代码:
    @interface MyTableViewCell : UITableViewCell
     
    @property (nonatomic, weak) IBOutlet UILabel *firstLabel;
    @property (nonatomic, weak) IBOutlet UILabel *secondLabel;
    @end
  3.  在MyTableViewCell.m中加入以下代码:
    @synthesize firstLabel, secondLabel;
  4. 在实现数据源的源文件中添加下面这行代码:
    #import "MyTableViewCell.h"
  5. 在Identityinspector面板中,设置单元格的Class属性为MyTableViewCell。
  6. 在Connectioninspector面板中,为两个outlet关联响应的label标签。
    (apple官方文档译文)深入理解表视图的单元格(cells)- A Closer Look at Table View Cells_第6张图片
  7. 实现数据源方法:tableView:cellForRowAtIndexPath:像列表5-4这样
    列表5-4

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyIdentifier"];
     
        cell.firstLabel.text = [NSString stringWithFormat:@"%d", indexPath.row];
        cell.secondLabel.text = [NSString stringWithFormat:@"%d", NUMBER_OF_ROWS - indexPath.row];
     
        return cell;
    }

    代码通过geter和setter存取器的方法去获得俩个label并修改其内容。

创建静态内容单元格的技巧

在这部分中,你将创建一个由静态单元格组成的表视图。在程序运行的时,从storyboard中将表视图加载进来,这是表视图会立即获取这些单元格,并将他们分节排列出来。如图5-7.

图5-7多行单元格表视图

(apple官方文档译文)深入理解表视图的单元格(cells)- A Closer Look at Table View Cells_第7张图片


像创建动态内容单元格列表一样,首先要为你的项目增加一个UITableViewController子类,然后分别为第一个单元格的label和最后一个单元格的滑块、滑块值lable声明一个outlet属性,如列表5-5.

列表5-5 为静态内容单元格对象声明outlet属性

@interface DetailViewController : UITableViewController
 
@property (strong, nonatomic) id detailItem;
@property (weak, nonatomic) IBOutlet UILabel *masterRowLabel;
@property (weak, nonatomic) IBOutlet UILabel *sliderValueLabel;
@property (weak, nonatomic) IBOutlet UISlider *slider;
 
- (IBAction)logHello;
- (IBAction)sliderValueChanged:(UISlider *)slider;
 
@end

在storyboard中,从library窗口中拖拽一个Table View Controller控件,选择表视图,在Attributes inspector面板中做如下设置:

  1. 将Content属性下拉列表的值设为Static Cells。
  2. 将sections属性的值设为2.
  3. 将Style属性下拉列表的值设为Grouped。
对于表视图中的每个节,通过Attributes inspector面板,设置节头。对于每个cell单元格,完成以下步骤:

  1. 删除第一个节中的其中两个单元格,仅留一个。删除第二个节中的其中一个单元格,留两个。
  2. 分别按照需求为每个单元格增加高度。
    没有必要为每个cell单元格设置重用标示符,因为在编码的时候你将不会实现数据源方法;tableView:cellForRowAtIndexPath:
  3. 从library为每个单元格拖拽相应的控件,并进行排列,如图5-7
  4. 为这些控件设置需要的属性
    这个例子中滑块的值范围是0-10,初始值为7.5.

选择表视图控制器(the table view controller),然后在Connection inspector面板中 ,将三个输出口(outlets)与相对应的控件连线,如图5-8。同时实现列表5-5中声明的两个动作方法,并将它们与button和slider控件连线。

图5-8 为你的静态内容单元格内容连线

(apple官方文档译文)深入理解表视图的单元格(cells)- A Closer Look at Table View Cells_第8张图片


接下来为静态单元格列表部署数据,在DetailViewController.m文件中实现一个configureView()方法。在这个例子中detailItem的值为由主视图控制器中prepareForSegue:sender:方法传递过来的一个NSString类型的字符串。这个字符串为单元格的数量。

列表5-6 为用户界面设置数据

- (void)configureView
{
    if (self.detailItem) {
        self.masterRowLabel.text = [self.detailItem description];
    }
    self.sliderValueLabel.text = [NSString stringWithFormat:@"%1.1f", self.slider.value];
}

在DetailViewController.m文件中的viewDidLoad方法和setDetailItem方法中调用configureView方法。

通过编程的方式为单元格视图添加子视图

一个表视图中的cell单元格都是也一个视图(UITableViewCell继承自UIView)。作为一个视图,它拥有一个contentView属性。可以将子视图通过单元格contentView属性添加到单元格中。然后调整它们的位置,这里的坐标是相对于它们的父视图坐标来定位。你可以在编程中部署他们,也可以像前面叙述那样在Interface Builder中部署。

 

用编程的方式部署相对来说比较简单。它不需要你去自定义一个UITableViewCell的子类去控制所有自定义视图的实现。如果你使用这种途径,尽可能的避免将这些子视图设为透明的,透明视图影响滑动时的性能,子视图应该是不透明的,并且跟cell单元格拥有同样的背景颜色。如果表视图的单元格是可选的,当单元格选中的时,一定要确保它的子视图也为选中状态。如果子视图设置了highlighted属性,它的子视图会自动变成选中状态。

 

你可能想自动定义你的文本和图像在cell单元格中的位置。比如,你想让图像向右对齐,主标题和副标题紧靠着图像的左侧。像图5-9这样。

图5-9 为cell单元格自定义子视图

(apple官方文档译文)深入理解表视图的单元格(cells)- A Closer Look at Table View Cells_第9张图片


列表5-7的代码例子,展示了数据源如何通过编程的方式来绘制和排版表视图的cell单元格。在tableView:cellForRowAtIndexPath:方法中,首先检查表视图中是否已经存在带有重用标示符的cell单元格对象。如果不存在,数据源将创建一个,并为它添加两个label标签和一个imageView视图作为其子视图,这里注意子视图的坐标是相对于它的父视图坐标。然后为这些对象设置属性。

 

列表5-7为cell单元格的内容视图添加子视图

#define MAINLABEL_TAG 1
#define SECONDLABEL_TAG 2
#define PHOTO_TAG 3
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    static NSString *CellIdentifier = @"ImageOnRightCell";
 
    UILabel *mainLabel, *secondLabel;
    UIImageView *photo;
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
 
        mainLabel = [[[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 220.0, 15.0)]];
        mainLabel.tag = MAINLABEL_TAG;
        mainLabel.font = [UIFont systemFontOfSize:14.0];
        mainLabel.textAlignment = UITextAlignmentRight;
        mainLabel.textColor = [UIColor blackColor];
        mainLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
        [cell.contentView addSubview:mainLabel];
 
        secondLabel = [[[UILabel alloc] initWithFrame:CGRectMake(0.0, 20.0, 220.0, 25.0)]];
        secondLabel.tag = SECONDLABEL_TAG;
        secondLabel.font = [UIFont systemFontOfSize:12.0];
        secondLabel.textAlignment = UITextAlignmentRight;
        secondLabel.textColor = [UIColor darkGrayColor];
        secondLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
        [cell.contentView addSubview:secondLabel];
 
        photo = [[[UIImageView alloc] initWithFrame:CGRectMake(225.0, 0.0, 80.0, 45.0)]];
        photo.tag = PHOTO_TAG;
        photo.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
        [cell.contentView addSubview:photo];
    } else {
        mainLabel = (UILabel *)[cell.contentView viewWithTag:MAINLABEL_TAG];
        secondLabel = (UILabel *)[cell.contentView viewWithTag:SECONDLABEL_TAG];
        photo = (UIImageView *)[cell.contentView viewWithTag:PHOTO_TAG];
    }
    NSDictionary *aDict = [self.list objectAtIndex:indexPath.row];
    mainLabel.text = [aDict objectForKey:@"mainTitleKey"];
    secondLabel.text = [aDict objectForKey:@"secondaryTitleKey"];
    NSString *imagePath = [[NSBundle mainBundle] pathForResource:[aDict objectForKey:@"imageKey"] ofType:@"png"];
    UIImage *theImage = [UIImage imageWithContentsOfFile:imagePath];
    photo.image = theImage;
 
    return cell;
}

当数据源创建cell单元格的时候,它会为每个单元格的子视图数值一个tag属性。使用这个tag值通过viewWithTag:方法可以在单元格的主视图层级中找到相对应的子视图的引用,之后便可以修改相应的内容。

 

这段代码创建了一个预定义样式的UITableViewCell对象(UITableViewCellStyleDefault)。因为预定义样式单元格的内容(textLabel,detailTextLabel,imageView)属性初始值为nil,所以你可以使用任何预定义样式的单元格作为你自定义单元格的模版。


你可能感兴趣的:(技术文档翻译)