自定義cell
概要:
自定義cell就是創建一個UITableViewCell的子類。
把cell上的控件創建都封裝在子類中,簡化UIViewController中的代 碼。
子視圖控件添加到cell的contentView上。
通信
cell中聲明一個Model類型的屬性,viewController中獲取到Model對象後賦值給cell的Model屬性。
cell中重寫Model的setter方法,把Model對象中的內容重新賦值給各
個控件 M和V不直接進行通信,C負責M和V之間進行通信。
創建新工程,添加加載數據方法和創建TableView
// 加載數據
- (void)setUpData
{
// 獲取文件路徑
NSString *path = [[NSBundle mainBundle] pathForResource:@"StudentList" ofType:@"plist"];
// 從該路徑 讀取文件
NSArray *arr = [NSArray arrayWithContentsOfFile:path];
// 字典轉化成模型 再放入字典或數組
self.dataArr = [NSMutableArray array];
for (NSDictionary *dic in arr) {
CellModel *model = [[CellModel alloc] init];
[model setValuesForKeysWithDictionary:dic];
[self.dataArr addObject:model];
[model release];
}
}
- (void)addTableView
{
UITableView *tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:(UITableViewStylePlain)];
tableView.dataSource = self;
tableView.delegate = self;
[self.view addSubview:tableView];
[tableView release];
}
自定義cell的步驟:
創建TableViewCell的子類
創建2個名爲MyTableViewCell、ManTableViewCell的繼承於TableViewCell的子類
重寫初始化方法
MyTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self addSubView];
}
return self;
}
ManTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self addSubview];
}
return self;
}
把要添加的控件 添加在cell的實現內容區域 contentView上面
MyTableViewCell
// 添加子視圖
- (void)addSubView
{
self.imageV = [[UIImageView alloc] initWithFrame:CGRectMake(kMargin, kMargin, kImageWidth, kImageHeight)];
self.imageV.backgroundColor = [UIColor redColor];
[self.contentView addSubview:self.imageV];
[self.imageV release];
self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.imageV.right + kMargin, self.imageV.top, (kScreenWidth - 3*kMargin - self.imageV.width), (kImageHeight - 2*kLabelMargin)/3)];
self.nameLabel.backgroundColor = [UIColor greenColor];
[self.contentView addSubview:self.nameLabel];
[self.nameLabel release];
self.phoneLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.nameLabel.left, self.nameLabel.bottom + kLabelMargin, self.nameLabel.width, self.nameLabel.height)];
self.phoneLabel.backgroundColor = [UIColor greenColor];
[self.contentView addSubview:self.phoneLabel];
[self.phoneLabel release];
self.genderLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.phoneLabel.left, self.phoneLabel.bottom + kLabelMargin, self.phoneLabel.width, self.phoneLabel.height)];
self.genderLabel.backgroundColor = [UIColor greenColor];
[self.contentView addSubview:self.genderLabel];
[self.genderLabel release];
}
ManTableViewCell
- (void)addSubview
{
self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 40)];
self.nameLabel.backgroundColor = [UIColor orangeColor];
[self.contentView addSubview:self.nameLabel];
[self.nameLabel release];
}
把系統的cell 替換成 自定義cell完成
MyTableViewCell
static NSString *identifier = @"GirlCell";
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[MyTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
ManTableViewCell
static NSString *identifier = @"ManCell";
ManTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[ManTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
多種類型的cell混合使用
注意:
通常我們會在tableView:cellForRowAtIndexPath:方法中根據不同的。
Model來決定使用什麼類型的cell 每種類型的cell要定義不同的重用標識符 。
cell重用的時候會根據重用標識從重用隊列中取用哪種類型的cell。
新建一個cellModel繼承於NSObject,聲明它的屬性
// 名字
@property (nonatomic, retain) NSString *name;
// 性別
@property (nonatomic, retain) NSString *gender;
// 手機號
@property (nonatomic, retain) NSString *phoneNumber;
把它聲明成tableViewcell子類的屬性,見上文截圖中。
用model在- (UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath方法中判斷用哪種cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 根據model的值進行判斷 顯示不同的cell
CellModel *model = self.dataArr[indexPath.row];
if ([model.gender isEqualToString:@"女"]) {
static NSString *identifier = @"GirlCell";
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[MyTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
}
// 在給model賦值的同時 咱們希望 也給cell上控件完成賦值
cell.model = model;
return cell;
} else {
static NSString *identifier = @"ManCell";
ManTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[ManTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
}
cell.model = model;
return cell;
}
}
這樣就能達到下面的效果了
cell自適應高度
獲取文本高度:(iOS7)
- (CGRect)boundingRectWithSize:(CGSize)size options:
(NSStringDrawingOptions)options attributes:(NSDictionary
*)attributes context:(NSStringDrawingContext *)context
注意:
計算文本高度是所用的字體要和label顯示時用的字體一致。
label的寬度要和計算時使用的限定寬度一致。
這樣才能保證文本顯示在label中時,label高度恰巧夠。
例題:實現如下效果,label正好是適應文字大小
新建工程,創建一個UIlabel
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 300, 100)];
label.backgroundColor = [UIColor orangeColor];
label.font = [UIFont systemFontOfSize:16];
label.text = @" 我崇拜高尚的生命的祕密。我崇拜這生命在降生、成長、戰鬥、傷殘、犧牲時迸濺出的鋼花焰火。我崇拜一個活靈靈的生命在崇山大河,在海洋和大陸上飄蕩的自由。是的,生命就是希望。它飄蕩無定,自由自在,它使人類中總有一支血脈不甘於失敗,九死不悔地追尋着自己的金牧場。";
[self.window addSubview:label];
[label release];
構建字體大小的字典 UIFont,NSFontAttributeName UIFont 在系統中的屬性的名字(key)
// 參數size
// 寬度 跟你的label一邊寬
// 高度 填一個這段字最大高度
// CGFLOAT_MAX 最大的float數
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:[UIFont systemFontOfSize:16], NSFontAttributeName, nil];
CGRect frame = [label.text boundingRectWithSize:CGSizeMake(300, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin) attributes:dic context:nil];
NSLog(@"%f", frame.size.height);
// 更改label的高度
CGRect temp = label.frame;
temp.size.height = frame.size.height;
label.frame = temp;
label.numberOfLines = 0;
[self.window addSubview:label];
[label release];
這樣就能簡單實現label適應文字的大小了
那怎麼讓cell自適應高度呢?怎麼解決cell的複用問題呢?
例題:
新建工程,添加加載數據方法,添加表視圖
- (void)setUpData
{
NSString *path = [[NSBundle mainBundle] pathForResource:@"NewsData" ofType:@"plist"];
NSDictionary *dicData = [NSDictionary dictionaryWithContentsOfFile:path];
self.dataArr = [NSMutableArray array];
NSArray *newsArr = dicData[@"news"];
for (NSDictionary *dic in newsArr) {
NewsModel *model = [[NewsModel alloc] init];
[model setValuesForKeysWithDictionary:dic];
// 給點擊狀態賦值初值
model.isSelect = NO;
[self.dataArr addObject:model];
[model release];
}
}
- (void)addTableView
{
UITableView *tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:(UITableViewStylePlain)];
tableView.dataSource = self;
tableView.delegate = self;
[self.view addSubview:tableView];
[tableView release];
}
創建NewsModel,聲明屬性
@property (nonatomic, retain) NSString *title;
@property (nonatomic ,retain) NSString *summary;
// 標識選中的狀態
@property (nonatomic, assign) BOOL isSelect;
創建NewsTableViewCell,重寫初始化方法,添加視圖
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self addSubview];
}
return self;
}
- (void)addSubview
{
self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(kMargin, kMargin, kLabelWidth, kLabelHeight)];
self.titleLabel.backgroundColor = [UIColor cyanColor];
[self.contentView addSubview:self.titleLabel];
[self.titleLabel release];
self.summaryLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.titleLabel.left, self.titleLabel.bottom + kMargin, self.titleLabel.width, self.titleLabel.height)];
self.summaryLabel.backgroundColor = [UIColor orangeColor];
// 多行顯示
self.summaryLabel.numberOfLines = 0;
// 設置字體大小
self.summaryLabel.font = [UIFont systemFontOfSize:16];
[self.contentView addSubview:self.summaryLabel];
[self.summaryLabel release];
self.imageV = [[UIImageView alloc] initWithFrame:CGRectMake(self.titleLabel.right + kMargin, kMargin, 3 * kMargin, 3 * kMargin)];
[self.contentView addSubview:self.imageV];
[self.imageV release];
}
實現協議中的兩個方法
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = @"MyCell";
NewsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[NewsTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
}
NewsModel *model = self.dataArr[indexPath.row];
cell.model = model;
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.dataArr.count;
}
實現cell的點擊方法
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// 取出點擊 cell對應的model
NewsModel *model = self.dataArr[indexPath.row];
// 取出被點擊的cell
NewsTableViewCell *cell = (NewsTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
// 更改點擊的狀態
model.isSelect = !model.isSelect;
if (model.isSelect == YES) {
// 更改cell上的圖片
cell.imageV.image = [UIImage imageNamed:@"select"];
} else {
// 更改cell上的圖片
cell.imageV.image = [UIImage imageNamed:@"cancel"];
}
}
重寫model的setter方法
- (void)setModel:(NewsModel *)model
{
if (_model != model) {
[_model release];
_model = [model retain];
}
self.titleLabel.text = model.title;
self.summaryLabel.text = model.summary;
// 利用model中的點選狀態 解決cell複用問題
// 需要每次被複用的cell 再進行一次與狀態對應的賦值
if (model.isSelect == YES) {
self.imageV.image = [UIImage imageNamed:@"select"];
} else {
self.imageV.image = [UIImage imageNamed:@"cancel"];
}
// 獲取字符串的高度
CGFloat summaryHeight = [NewsTableViewCell cellHeightForModel:model];
// 改變一下label的高度
CGRect frame = self.summaryLabel.frame;
frame.size.height = summaryHeight;
self.summaryLabel.frame = frame;
}
計算高度的類方法
+ (CGFloat)cellHeightForModel:(NewsModel *)model
{
// 創建字體大小的字典
NSDictionary *fontDic = @{NSFontAttributeName:[UIFont systemFontOfSize:16]};
// 計算字符串高度
CGRect textRect = [model.summary boundingRectWithSize:CGSizeMake(kLabelWidth, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin) attributes:fontDic context:nil];
return textRect.size.height;
}
返回cell的動態高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NewsModel *model = self.dataArr[indexPath.row];
CGFloat labelHeight = [NewsTableViewCell cellHeightForModel:model];
// 上邊距 + topLabel + 中間間距 + 動態label高度 + 下邊距
return 20 + labelHeight + 40 + 10;
}
這樣就解決了cell的自適應高度和cell的複用問題。