三十而立,从零开始学ios开发(十二):Table Views(中)UITableViewCell定制
我们继续学习Table View的内容,这次主要是针对UITableViewCell,在前一篇的例子中我们已经使用过UITableViewCell,一个默认的UITableViewCell包含imageView、textLabel、detailTextLabel等属性,但是很多时候这些默认的属性并不能满足需要,其实更多的时候我们想自己制定UITableViewCell的内容,这篇学习的就是制定自己的UITableViewCell。
UITableViewCell继承自UIView,因此它可以加载任意的subView在上面,基于这一点,我们就可以定制自己的UITableViewCell了。制定UITableViewCell有2种方法,一种是写代码生成UITableViewCell控件上的内容,另一种是拖控件到一个UITableViewCell控件上,这两种方法都会用一个例子来进行说明。
首先我们使用code的方法,来定制自己的UITableViewCell
1)创建一个新的项目,template选择Single View Application,命名为Cells
2)添加Table View,连接delegate和data source到File's Owner
选中BIDController.xib文件,从Object Library中拖一个Table View到view上,然后选中view中的table view,打开Connections Inspector,拖动dataSource和delegate右边的小圆圈到File's Owner上
3)添加Cell
在Project navigator中选中Cells文件夹,然后command+N,添加一个新的文件。在弹出的对话框中,左边选择Cocoa Touch,右边选择Objective-C,然后点击Next
在下一个对话框中把Class命名为BIDNameAndColorCell,Subclass of选择UITableViewCell,然后点击Next
在下一个对话框中选择Create,完成创建
BIDNameAndColor文件继承自UITableViewCell,我们将先对其进行定制,添加一些我们需要的控件,当BIDViewController中调用table cell的时候,就直接调用这个文件即可。
4)添加BIDNameAndColorCell代码
打开BIDNameAndColorCell.h文件,添加如下代码
#import <UIKit/UIKit.h> @interface BIDNameAndColorCell : UITableViewCell @property (copy, nonatomic) NSString *name; @property (copy, nonatomic) NSString *color; @end
我们将在table view cell中显示2个字符串,一个是name,另一个是color,显示的内容会从之后定义的NSArray中读取。(注意,我们这里使用的是copy,而不是通常使用的strong,使用copy的好处是不会改变原有的字符串中的内容,当然在这里我们也不会改变字符串的内容,你如果使用了strong也问题不大,不过书上的建议是如果一个property是NSString,最好使用copy)
接着编辑BIDNameAndColorCell.m文件,添加下面的code
#import "BIDNameAndColorCell.h" #define kNameValueTag 1 #define kColorValueTag 2 @implementation BIDNameAndColorCell @synthesize name; @synthesize color;
首先添加2个常量kNameValueTag和kColorValueTag,这2个常量用来标识table view cell中的name和color。接着就是name和color的synthesize了,和之前例子中的一样。
下面修改BIDNameAndColorCell.m中已存在的initWithStyle:reuseIdentifier:方法
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { // Initialization code CGRect nameLabelRect = CGRectMake(0, 5, 70, 15); UILabel *nameLabel = [[UILabel alloc] initWithFrame:nameLabelRect]; nameLabel.textAlignment = UITextAlignmentRight; nameLabel.text = @"Name:"; nameLabel.font = [UIFont boldSystemFontOfSize:12]; [self.contentView addSubview:nameLabel]; CGRect colorLabelRect = CGRectMake(0, 26, 70, 15); UILabel *colorLabel = [[UILabel alloc] initWithFrame:colorLabelRect]; colorLabel.textAlignment = UITextAlignmentRight; colorLabel.text = @"Color:"; colorLabel.font = [UIFont boldSystemFontOfSize:12]; [self.contentView addSubview:colorLabel]; CGRect nameValueRect = CGRectMake(80, 5, 200, 15); UILabel *nameValue = [[UILabel alloc]initWithFrame:nameValueRect]; nameValue.tag = kNameValueTag; [self.contentView addSubview:nameValue]; CGRect colorValueRect = CGRectMake(80, 25, 200, 15); UILabel *colorValue = [[UILabel alloc]initWithFrame:colorValueRect]; colorValue.tag = kColorValueTag; [self.contentView addSubview:colorValue]; } return self; }
上面的代码应该还是比较容易理解的吧,创建了4个UILabel,设置完UILabel的属性后,把他们加入到UITableViewCell中,table view cell有一个默认的view叫做contentView,contenView负责容纳其他的subView,因此上面code中所创建的4个UILabel都会添加到contentView中,使用的语句是[self.contentView addSubview:colorValue]
再说一下上面创建的4个UILabel在这个例子中的作用,nameLabel和colorLabel的作用是纯粹的Label,它们的值不会在改变,在这只它们属性的使用已经分别为它们赋值了。nameValue和colorValue这2个label是用来显示NSArray中的值的,这也是为什么只有这2个label会有property,因为它们需要改变值。另外在code中还未这2个label制定了tag,这个tag的具体作用是通过它来标识具体的label,添加下面的code,就会有所了解。
- (void)setName:(NSString *)n { if(![n isEqualToString:name]) { name = [n copy]; UILabel *nameLabel = (UILabel *)[self.contentView viewWithTag:kNameValueTag]; nameLabel.text = name; } } - (void)setColor:(NSString *)c { if(![c isEqualToString:color]) { color = [c copy]; UILabel *colorLabel = (UILabel *)[self.contentView viewWithTag:kColorValueTag]; colorLabel.text = color; } }
@synthesize会帮我们自动创建get和set方法,但在这里我们需要自己写set方法,因此通过上面的code来覆盖系统自动为我们生成的set方法。2个set方法的实现是一样的。首先比较新赋值的字符串和旧的字符串是否一样,如果不一样就进行赋值。然后需要解释的就是这句话
UILabel *colorLabel = (UILabel *)[self.contentView viewWithTag:kColorValueTag];
如果找到table view cell中的控件?即使使用刚才我们创建的2个常量,因为每一个控件的tag值都是不一样的,因此根据tag值就可以很方便的找到我们需要的控件,viewWithTag返回的类型是(UIView *),所以我们将类型强制转换成(UILable *),就可以得到我们需要的Label了,然后对Label进行赋值。
5)添加BIDViewController代码
打开BIDViewController.h,添加如下代码
#import <UIKit/UIKit.h> @interface BIDViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> @property (strong, nonatomic) NSArray *computers; @end
很简单,不解释。
打开BIDViewController.m,添加如下代码
#import "BIDViewController.h" #import "BIDNameAndColorCell.h" @implementation BIDViewController @synthesize computers; ...... - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. NSDictionary *row1 = [[NSDictionary alloc] initWithObjectsAndKeys: @"MacBook", @"Name", @"White", @"Color", nil]; NSDictionary *row2 = [[NSDictionary alloc] initWithObjectsAndKeys: @"MacBook Pro", @"Name", @"Silver", @"Color", nil]; NSDictionary *row3 = [[NSDictionary alloc] initWithObjectsAndKeys: @"iMac", @"Name", @"Silver", @"Color", nil]; NSDictionary *row4 = [[NSDictionary alloc] initWithObjectsAndKeys: @"Mac Mini", @"Name", @"Silver", @"Color", nil]; NSDictionary *row5 = [[NSDictionary alloc] initWithObjectsAndKeys: @"Mac Pro", @"Name", @"Silver", @"Color", nil]; self.computers = [[NSArray alloc] initWithObjects:row1, row2, row3, row4, row5, nil]; } - (void)viewDidUnload { [super viewDidUnload]; // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; self.computers = nil; }
首先我们引入BIDNameAndColor的头文件,之后我们将会创建它的实例,在viewDidLoad中,创建了5个NSDictionary,用于保存name和color名值对,使用的方法是initWithObjectsAndKeys,就是说下面的内容前一个作为object,后一个作为key,举个例子,最后一个NSDictionary中,@"Mac Pro"就是object,@"Name"就是key。最后将5个NSDictionary保存到NSArray中(computers中)
在添加下面的code
#pragma mark - #pragma mark Table Data Source Methods - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.computers count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellTableIdentifier = @"CellTableIdentifier"; BIDNameAndColorCell *cell = [tableView dequeueReusableCellWithIdentifier:CellTableIdentifier]; if(cell == nil) { cell = [[BIDNameAndColorCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellTableIdentifier]; } NSUInteger row = [indexPath row]; NSDictionary *rowData = [self.computers objectAtIndex:row]; cell.name = [rowData objectForKey:@"Name"]; cell.color = [rowData objectForKey:@"Color"]; return cell; }
tableView:numberOfRowsInSection: 返回section中的行数
tableView:cellForRowAtIndexPath: 这个方法稍微有些不同,它吃创建了一个BIDNameAndColorCell的对象,而不是创建默认的UITableViewCell,这样就可以直接使用我们自己定义的cell了,这里虽然也指定了UITableViewCellStyleDefault,但是不会起作用,因为我们自己定义了cell
之后的代码应该很好理解,很直观。
6)编译运行
7)使用UITableViewCell控件,创建项目,添加table view,连接File's Owner
现在我们使用第二种方法来定制UITableViewCell,最终的效果和上面的例子一样,创建一个新的项目,同样选择Single View Application,命名为Cells2
添加Table View,连接delegate和data source到File's Owner
8)添加BIDNameAndColorCell文件并编辑
和上面的例子一样,添加BIDNameAndColorCell文件
打开BIDNameAndColorCell.h文件,添加如下代码
#import <UIKit/UIKit.h> @interface BIDNameAndColorCell : UITableViewCell <UITableViewDelegate, UITableViewDataSource>
@property (copy, nonatomic) NSString *name; @property (copy, nonatomic) NSString *color; @property (strong, nonatomic) IBOutlet UILabel *nameLabel; @property (strong, nonatomic) IBOutlet UILabel *colorLabel; @end
和之前有所不同的是,我们多添加了2个Outlet,因为之后要添加xib,因此这2个outlet会指向xib上了2个UILabel
打开BIDNameAndColorCell.m文件,添加如下代码
#import "BIDNameAndColorCell.h" @implementation BIDNameAndColorCell @synthesize name; @synthesize color; @synthesize nameLabel; @synthesize colorLabel; - (void)setName:(NSString *)n { if(![n isEqualToString:name]) { name = [n copy]; nameLabel.text = name; } } - (void)setColor:(NSString *)c { if(![c isEqualToString:color]) { color = [c copy]; colorLabel.text = color; } }
我们重新定义了setName和setColor,这里不需要使用tag,因为我们直接使用outlet就可以找到我们需要的UILabel,另外我们也没在initWithStyle:reuseIdentifier中创建4个Label,因为我们会在之后UITableViewCell控件中添加。
9)添加xib
在Project navigator中鼠标右键单击Cells2文件夹,然后选择New File...,在填出的对话框中左边选择User Interface,右边选择Empty,点击Next
之后的Device Family中选择iphone,点击Next
命名为BIDNameAndColorCell.xib,点击Create
在Project navigator中选中BIDNameAndColorCell.xib文件,因为我们创建的是Empy,因此GUI中什么都没有,在Object library中找到Table View Cell
拖到GUI中
选中view中的table view cell,打开Attributes inspector,找到Identifier,并设置为CellTableIdentifier
这个Identifier就是之前我们code中用到过的Identifier
10)向table view cell中添加控件
往table view cell中拖4个UILable
将左上方的UILabel命名为"Name:",然后设置为粗体(在attribute inspector中设置)
将左下方的UILabel命名为"Color:",然后设置为粗体(在attribute inspector中设置)
将右上方的UILabel拉到右边出现辅助线的位置
将右下方的UILabel拉到右边出现辅助线的位置
设置完成后的样子如下
(不必将右边2个Label的文字去掉,程序运行是会为它们重新赋值)
11)关联
接着我们将BIDNameAndColorCell.xib和BIDNameAndColorCell文件关联起来,选中GUI中的view,打开Identify inspector,将Class指定为BIDNameAndColorCell
还是选中view,切换到connections inspector,里面有colorLabel和nameLabel
将colorLabel和nameLabel拖到view中对应的UILabel上,关联起来(最右边的2个label)
12)写代码
打开BIDViewController.h文件,添加如下代码
#import <UIKit/UIKit.h> @interface BIDViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> @property (strong, nonatomic) NSArray *computers; @end
这个和上一个例子一样,不解释了,打开BIDViewController.m文件,添加如下代码
#import "BIDViewController.h" #import "BIDNameAndColorCell.h" @implementation BIDViewController @synthesize computers; ...... - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. NSDictionary *row1 = [[NSDictionary alloc] initWithObjectsAndKeys: @"MacBook Air", @"Name", @"Silver", @"Color", nil]; NSDictionary *row2 = [[NSDictionary alloc] initWithObjectsAndKeys: @"MacBook Pro", @"Name", @"Silver", @"Color", nil]; NSDictionary *row3 = [[NSDictionary alloc] initWithObjectsAndKeys: @"iMac", @"Name", @"Silver", @"Color", nil]; NSDictionary *row4 = [[NSDictionary alloc] initWithObjectsAndKeys: @"Mac Mini", @"Name", @"Silver", @"Color", nil]; NSDictionary *row5 = [[NSDictionary alloc] initWithObjectsAndKeys: @"Mac Pro", @"Name", @"Silver", @"Color", nil]; self.computers = [[NSArray alloc] initWithObjects:row1, row2, row3, row4, row5, nil]; } - (void)viewDidUnload { [super viewDidUnload]; // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; self.computers = nil; } #pragma mark - #pragma mark Table View Data Source Methods - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.computers count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellTableIdentifier = @"CellTableIdentifier"; static BOOL nibsRegistered = NO; if(!nibsRegistered) { UINib *nib = [UINib nibWithNibName:@"BIDNameAndColorCell" bundle:nil]; [tableView registerNib:nib forCellReuseIdentifier:CellTableIdentifier]; nibsRegistered = YES; } BIDNameAndColorCell *cell = [tableView dequeueReusableCellWithIdentifier:CellTableIdentifier]; NSUInteger row = [indexPath row]; NSDictionary *rowData = [self.computers objectAtIndex:row]; cell.name = [rowData objectForKey:@"Name"]; cell.color = [rowData objectForKey:@"Color"]; return cell; }
我们所要关心的就是tableView:cellForRowAtIndexPath这个方法中的if语句这段代码:
if(!nibsRegistered) {
UINib *nib = [UINib nibWithNibName:@"BIDNameAndColorCell" bundle:nil];
[tableView registerNib:nib forCellReuseIdentifier:CellTableIdentifier];
nibsRegistered = YES;
}
UINib通过xib文件的名字找到xib文件,然后tableView会调用这个xib文件,nibsRegistered保证只有第一次调用这个方法的时候会去寻找并载入xib,之后则不会。
13)编译运行
编译运行程序,得到的结果和之前的一个程序应该是一样的
14)总结
这篇文章使用2种方式定制了UITableViewCell,一种是代码实现,另一种是传统的拖控件实现,2种方法应该说各有利弊,不一定拖控件就是好的,我应该已经预感到以后的界面布局应该都使用写代码来实现,已经有好几个博友通过我的例子学习但是得到的界面上面控件的布局出现了差错,目前的原因是他们使用的模拟器和我的不一样,我使用的是iPhone 5.0 Simulator,他们可以是用其他的模拟器,但是我觉得照成差错的原因是因为学习到现在我们都是使用拖控件的方法来布局的,这个会造成差错,如果都是用code来实现布局,那么情况应该会变得一致。
下一篇的内容会讲到Table View的Section、index、搜索栏等等,内容比较多也比较实用,实现的效果如下
我会尽快写完放上来的,谢谢大家的关注,有任何问题大家留言吧,如果我知道的话,我会尽量回答,谢谢!