IOS开发入门(12)-表视图I:基础知识
在前面几部分中,主屏幕只能展示一个汽车对象的信息。而在实际iOS中,一次显示多条数据并实现滚动查看是十分常见的,例如通讯录、音乐以及其他多个以表格样式展示数据的应用程序。
表视图是开发者的工具包中至关重要的控件之一,当设计和创建那些运行在屏幕空间不大的iPhone/iPad设备上的应用陈旭时,该视图的作用尤为重要。与UINavigationController结合使用时,表视图控件能建议地展示有着层次结构的数据,轻松实现导航。表视图允许用户便捷地访问对象概要列表。本节将介绍有关表视图的一些基本知识。
表视图介绍
3类与2协议
- UITableViewController是主视图控制器,拥有很少几个有关视图的属性,并且只有一个方法。大多数工作由两个控制器所遵从的协议来实现。通过实现这些协议的方法,可以显示表视图的单元格,在某些情况下,实现第用户交互进行响应。
- UITableViewDataSouce提供的是从对象获取的数据详情,这些数据会在与表中的每个表格段外和表格行对应的每一个单元格中进行展示。此外提供了多个可选的方法,用于实现单元格的添加、删除和排序。
- UITableViewDelegate支持表视图的多个行为。利用UITableViewDelegate,可以实现任何操作:从配置某个单元格的高度或表头视图,以及支持编辑和突出强调功能。
- UITableView实现所有的功能,能够配置并显示表格视图的各个部分。它拥有一些额外的方法和属性,能够实现添加、移动和删除单元格;更新数据,手动实现视图内容的滚动;访问视图的其他部分;配置索引等。当然,其中的某些功能,最好由视图数据源(UITableDataSouce)和委托(UITableViewDelegate)来实现。
- UITableViewCell表示在表视图中显示的单行。通过“标题和副标题,添加图片”的不同配置,能形成4种不同的、系统自带的单元格样式。此外,也可以自定义单元格的样式。
表视图的常见行为
- 设置——对表视图的表头视图、表尾视图、表格段、每个表格段的表头和/或表尾、每组中单元格的个数进行设置。
- 显示——显示可见的表格
- 滚动——计算出大暖阁何时进入屏幕并负责加载它们。表视图管理单元格的创建,以及单元格的重用,它会创建缓存池,存储已创建的单元格,并负责权衡如何为赋予了新的数据之后、即将滚入屏幕的单元格的配置样式。
- 选中——更新单元格的选中状态或者负责更新表视图配置的单元格。根据选中状态来调用相应的委托方法。
阶段I:学习一下表视图的基本用法
创建空白文件
首先我们先创建一个Empty Application的模板(当然xcode6以后就没有这个了),但是没关系,我们正常创建一个 Single View Application模板,然后将 ViewController.m、ViewController.h和Main.storyboard删除。
(方法查自于http://www.cocoachina.com/bbs/read.php?tid=231701)
这样就创建了一个Empty Application了。
接下来在xcode中选择file|new,添加一个iphone故事板文件,选中user interface类别中的storyboard,添加到文件中,将.plist将Main storyboard file base name 那项删掉中的Main改成刚才添加的那个故事面版文件。
我们得到了一个什么都没有的故事面板文件,这时候我们将一个表视图控制器(Table View Controller)拖入到故事面板文件中,然后点击刚才放入的故事面板,将 Is Initial View Controller 选中
好了,创建完了
现在,最简单的诗经就是创建一些预设的单元格了
- 选择主故事版(VIew Controller)并选择Attributes检查器
- 在Attributes检查器的顶端的Table View部分,将Content下拉菜单选为Static Cells(静态单元格),可以看到,故事面板表视图图像由一个变成了三个。如下图(嘿嘿嘿,升级版动态图)
运行后,虽然看起来没变,但是点击上面三行表格时,表格会变成灰色表示选中,而之前是不行的
创建单元格
有时候我们想要创建预先定义的单元格。但更常见的情况是,我们想要显示数量不定、数量可变的数据项列表,因此我们就需要提前知道其内容。上面我们创建的表视图是使用静态单元格。在表格中,它们的表现类似于静态视图。但有时候,我们不知道需要显示多少单元格,以及如何来显示它们。此时,我们就需要使用动态原型。
当然,使用什么类型的单元格取决于我们要做什么,像setting显然我们是知道需要多少单元格的就使用静态单元格,而类似于备忘录的,则是使用动态原型。
下面我们将进行动态原型的演练
- 将表格类型改回原来的动态原型,将多出来的两个单元格直接删掉就行了;
选中Table View Cell,将Identifiler设置为MyCell
如果没有设置为MyCell(其他名字也行,总之要有名字),会有如下警告
这是在告诉我们:“原型单元格(prototype cell)没有设置重用标识符。重用标识符(reuse identifier)能让表视图控制器判定创建和重用哪一种类型的单元格原型
现在我们需要编写代码告诉表视图需要创建多少单元格,并需要给这些单元格填充数据,因此,我们需要一个UITableViewDataSource。
创建步骤:(跟以前创建的差不多,不过有点变化)
- 创建新文件,选择Cocoa Touch类别,选择Objective-C类
返回故事版,选中表视图控制器(再次说一下就是Table View Controller),并且使用Identity检查器将该类设置为MyTableViewController
在MyTableViewController.m中会有警告,就是文件中有#warning,这个特殊的标记是要告诉xcode有些事需要处理。警告是要告诉我们还需要写一写代码。
分行和组
每个警告都告诉我们一些知识,有关表视图如何组织数据的方式。表视图的数据组(表格段)可以为0个或多个,每个表格段可以拥有0行或者多行的数据。个数从0开始计,看上去很奇怪,但是在表视图中,全控的数据集或仅仅是空的表格段都是有可能的
下面我们通过代码告诉表视图数据段有多少,以及每段数据含有多少行(其实从方法名称上就能大致知道怎么用了)
- 将MyTableViewController.m中的两个方法替换为如下代码
//这个方法是告诉表视图一共有多少数据段(该方法是可选的,如果没有提供该方法,则默认1)
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
//warning Incomplete implementation, return the number of sections
return 1;
}
//这个方法说明指定的数据段含有多少个数据项,这里我们弄5个
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//warning Incomplete implementation, return the number of rows
return 5;
}
在刚替换的两个方法下面的tableView:cellForRowAtIndexPath:方法中,将CellIndentifier的值由
@"Cell"
改为@"MyCell
。这告诉表视图创建的新的单元格时应用哪个原型。再次运行,这时候会有5个可以单击的单元格
生成单元格和索引路径(Index Path)
首先,我们需要知道表视图中的Index Path是什么。表视图数据显示为一个或多个组。每个行组被称为section(表格段)。表中的任何行可以根据它的组和行的数字,进行识别、予以区分。这个组合被称为index Path(索引路径),(我觉得可以简单的认为是一个动态的二维数组)
在表视图中,所以路径使用的是NSIndexPath类中的一个更通用、专门针对表视图的版本来表示索引路径。它由两个整数组成:第一个是表格段编号,第二个是行号。因为索引路径是对象,所以可以发送消息或使用点表示法来访问其部件。表视图使用一组特殊访问器,section和row让代码更容易读且更可维护
如果在表中有个NSIndexPath名为cellIndexPath,那么可以使用cellIndexPath.section来获取它的表格段标号,用cellIndexPath.row来访问他的行。
代码如下:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyCell" forIndexPath:indexPath];
NSString *myLabel = [NSString stringWithFormat:@"@Section: %ld Row: %ld",(long)indexPath.section,(long)indexPath.row];
cell.textLabel.text = myLabel;
return cell;
}
运行结果:
添加表格段
现在我们要像表格中添加表格段。要实现这一点,仅仅需要将numberOfSectionsInTableView:
的返回值从1改为2,运行这个程序,则会有两个含有5分元素的表格段,但是它们没有表格段的表头视图。
添加表格段的表头视图,只需要在numberOfSectionsInTableView:
方法的下方插入以下代码
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [NSString stringWithFormat:@"Section: %ld",(long)section];
}
效果如下图
可以试着添加更多的表格段或再每个表格段中添加更多的单元格。即使添加的数量非常大,表视图还是能工作。部分原因是,表视图仅仅会保留那些处于可见的单元格,以及一些额外的单元格行,从而实现快速的滚动。如果创建一个含有数千数据的表格,内存中一次只保留几十个数据。表视图创建他所需要的单元格,并回收已经不在屏幕上的单元格。这正是重用标识符的用处。
阶段II:替换Add/View场景
PS可以到ArnoldYUV ,我的git上下载代码
替换表视图控制器
首先,将Add/View场景替换为一个基于表视图控制器的场景:
- 打开故事面板,拖入一个表视图控制器,并将其放到已有的Edit/View场景的上方;
- 在我们的项目中添加两个新的Objective-C类:CarTableViewController继承自UITableViewController,CarTableViewCell继承自UITableViewCell,这里就不贴图了;
- 在故事面板中,将第一步添加的表视图控制器的类设置为CarTableViewController,并且将表视图的原型单元格的类设置为CarTableViewCell。保持原型单元格被选中,选择属性并将重用标识符设置为CarCell;
- 将Car对象的一个属性添加到汽车单元格中。这个属性用于单元格中要显示的数据,并且最终决定查看和编辑哪辆汽车。打开CarTableViewCell.h并添加如下代码:
#import <UIKit/UIKit.h>
@class Car;
@interface CarTableViewCell : UITableViewCell
@property (strong, nonatomic) Car *myCar;
@end
- 将Car.h导入CarTableViewCell.m文件中
- 打开CarTableViewController.m并添加如下代码,除了导入汽车模型类和汽车表视图单元格外,还需声明一个汽车数据数组,正如我们在ViewController.m中所实现的操作:
#import "CarTableViewController.h"
#import "Car.h"
#import "CarTableVIewCell.h"
@interface CarTableViewController ()
@end
@implementation CarTableViewController{
NSMutableArray *arrayOfCars;//这是用来存车的数组
}
- 将VIewController.m中的newCar:方法复制到CarTableController.m文件的底部,在@end的上面。然后删除updateLabel:withBaseString:andCount:调用
- 在CarTableViewController.m中,修改viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
arrayOfCars = [NSMutableArray new];
[self newCar:nil];
}
- 将表格段编号修改为1,将总的行数修改为arrayOfCars数组的大小:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
//根据有多少量车就设置多少单元格
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [arrayOfCars count];
}
- 在
tableView:cellForRowAtIndexPath:
中,确保静态的CellIdentifier正确设置。代码如下
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"CarCell";
//.. .. ..我忘了之前啥样子的.. .. .. 关键是要加上面这一句
//暂时这些 还有
}
- 切换到故事版,并改变导航控制器的指向
运行程序后。表视图控制器现在是Add/View场景。在继续进行之前,将导航栏的标题设置为CarValet,也可以从ViewController.m中复制本地化代码
添加汽车查看单元格
我们现在已经有了一辆汽车的数据了,以及一个现实这辆汽车的单元格。但是我们没有拥有视图元素来显示该数据。下一步就是修改原型单元格,从而显示汽车数据。步骤如下:
- 向Car对象中添加类为NSDate(很容易错写成NSData要注意)的属性dateCreated。在基类的初始化方法中将其设置为[NSDate date]
//在Car.h中
@property NSDate* dateCreated;//新加
//在Car.m中
- (id)init {
self = [super init];
if (self != nil) {
_year = kModelTYear;
_fuleAmount = 0.0f;
_dateCreated = [NSDate date];//新加
}
return self;
}
- (id)initWithMake:(NSString *)make
model:(NSString *)model
year:(int)year
fuelAmount:(float)fuelAmount {
self = [super init];
if (self !=nil) {
_make = [make copy];
_model = [model copy];
_year = year;
_fuleAmount = fuelAmount;
_dateCreated = [NSDate date];//新加
}
return self;
}
在故事板上,打开汽车表视图的单元格的Attributes检查器,并且将其设置为RightDetail样式(其实上面已经有图这么弄了),这是表格的样式会改变,会在坐标以黑色显示标题,并且在右边以浅灰色显示详情。将详情的字号设置为12。
向CarTableViewCell中添加一个公有方法,代码如下,注意要在.h文件中声明
//.h文件 增加
- (void)configureCell;
//.m文件
- (void)configureCell {
NSString *make = (self.myCar.make == nil) ? @"Unknown" : self.myCar