UITableView是一個比較復雜的控件,不過再負責也是由一些基礎的UIView組成的,它繼承自UIScrollView,並由很多重用的cell組成。當然為了效率UITableView還做了很多優化的措施,比如cell的重用等。
今天我們就一起來看看UITableView的最重要的組成部分UITableViewCell的一些細節以及分割線的問題。
一、UITableViewCell的高亮和選中
當我們打開一個tableView的頁面,上面布滿了一個個cell,tableview允許我們通過datasource為這些cell提供數據,通過delegate來控制點擊cell時候如何響應。查看UITableViewCell的幫助文檔我們可以看到它有兩個屬性highLighted、selected。這兩者之間到底又怎么樣的聯系呢?當我們點擊cell的時候都發生了什么呢?要達到這個目的,很簡單我們只要自定義一個cell繼承自UITableViewCell,然后重載它的以下兩個方法:
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated;
- (void)setSelected:(BOOL)selected animated:(BOOL)animated;
這兩個方法一個是設置cell的高亮狀態,另一個是設置cell的選中狀態,我們只需要在這兩個方法里面打印信息就可以看出點擊cell時這些狀態是怎么變化的了。下面直接上代碼:
SvTestTBCellViewController.m
//
// SvTestTBCellViewController.m
// SvTableviewCell
//
// Created by maple on 12/12/12.
// Copyright (c) 2012 maple. All rights reserved.
//
#import "SvTestTBCellViewController.h"
#import "SvTableViewCell.h"
@interface SvTestTBCellViewController () {
NSArray *_contentsArray;
}
@end
@implementation SvTestTBCellViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
self.view.backgroundColor = [UIColor whiteColor];
_contentsArray = [[NSArray alloc] initWithObjects:@"Content A", @"Content B", @"Content C", @"Content D", @"Content E", nil];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btn.frame = CGRectMake(0, 0, 100, 60);
btn.center = CGPointMake(160, 300);
[btn setTitle:@"Select C" forState:UIControlStateNormal];
[btn addTarget:self action:@selector(selectCellC:) forControlEvents:UIControlEventTouchUpInside];
[self.view insertSubview:btn aboveSubview:self.tableView];
//
// self.tableView.allowsMultipleSelection = YES;
self.tableView.separatorStyle = UITableViewCellSelectionStyleNone;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dealloc
{
[_contentsArray release];
[super dealloc];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
SvTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if (!cell) {
cell = [[SvTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.backgroundColor = [UIColor greenColor];
}
cell.indexPath = indexPath;
[cell setContentTitle:[_contentsArray objectAtIndex:indexPath.row]];
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"cell index: %d become selected!", indexPath.row);
}
#pragma mark - Button Action
- (void)selectCellC:(UIButton*)btn
{
[self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:0] animated:YES scrollPosition:UITableViewScrollPositionMiddle];
}
@end
SvTableViewCell.h
//
// SvTableViewCell.h
// SvTableviewCell
//
// Created by maple on 12/12/12.
// Copyright (c) 2012 maple. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface SvTableViewCell : UITableViewCell {
UILabel *_contentLbl;
}
@property (nonatomic, retain) NSIndexPath *indexPath;
- (void)setContentTitle:(NSString*)content;
@end
SvTableViewCell.m
//
// SvTableViewCell.m
// SvTableviewCell
//
// Created by maple on 12/12/12.
// Copyright (c) 2012 maple. All rights reserved.
//
#import "SvTableViewCell.h"
@implementation SvTableViewCell
@synthesize indexPath;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
_contentLbl = [[UILabel alloc] initWithFrame:self.contentView.bounds];
_contentLbl.backgroundColor = [UIColor clearColor];
_contentLbl.textAlignment = UITextAlignmentCenter;
_contentLbl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_contentLbl.font = [UIFont systemFontOfSize:24];
[self.contentView addSubview:_contentLbl];
[_contentLbl release];
}
return self;
}
- (void)dealloc
{
self.indexPath = nil;
[super dealloc];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
NSLog(@"set cell %d Selected: %d", indexPath.row, selected);
if (selected) {
_contentLbl.textColor = [UIColor whiteColor];
}
else {
_contentLbl.textColor = [UIColor blackColor];
}
}
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
[super setHighlighted:highlighted animated:animated];
NSLog(@"set cell %d highlighted: %d", indexPath.row, highlighted);
if (highlighted) {
_contentLbl.textColor = [UIColor whiteColor];
}
else {
_contentLbl.textColor = [UIColor blackColor];
}
}
- (void)setContentTitle:(NSString*)content
{
_contentLbl.text = content;
}
// 自繪分割線
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillRect(context, rect);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:0xE2/255.0f green:0xE2/255.0f blue:0xE2/255.0f alpha:1].CGColor);
CGContextStrokeRect(context, CGRectMake(0, rect.size.height - 1, rect.size.width, 1));
}
@end
測試的程序很簡單,直接重載了這兩個方法,打印設置的選中和高亮的狀態。當我們點擊任何一個cell的時候,輸出如下:

我們可以看出,當我們點擊cell的時候,其實是先設置cell的高亮狀態為YES,然后松手的時候再將cell的高亮狀態設置為NO,接着才是設置cell的選中狀態為YES,最后才會去調用delegate中的tableview:didSelectRowAtIndexPath:方法。
此處我們delegate的tableview:didSelectRowAtIndexPath:方法中只是做了打印(沒有遵照設計規范,在該方法中取消選中),這個時候已經有一個cell處於選中狀態時,我們再去點擊另外一個cell時,輸出如下:

通過截圖我們可以看出,前兩布還是和第一次點擊cell時類似,但是緊接着是首先設置前一個cell為非選中狀態,然后在設置當前點擊的cell為選中狀態,最后再調用delegate的方法。為什么會先取消上一個cell的選中狀態呢?因為tableView默認是不支持多選的,我們可以通過設置allowsMultipleSelection為YES來設置支持多選。
總結上面兩種情況,我們發現cell的高亮狀態是不能持久的,即tap的時候會變成高亮,松手的時候就會自動設置為非高亮狀態。而cell的選中狀態則是可以持久的,我們不去觸發它改變狀態,則選中狀態就不會改變。
說了這么多,這兩個狀態在實際應用有什么作用呢?讓我們思考一個簡單的情形,通常cell非選中且非高亮狀態時候cell上的字體是黑色的,但是高亮或者選中狀態時我們可能希望改變字體的顏色為白色或者其他顏色,這個時候就可以通過重載這兩個方法來實現,下面是代碼片段:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
NSLog(@"set cell %d Selected: %d", indexPath.row, selected);
if (selected) {
_contentLbl.textColor = [UIColor whiteColor];
}
else {
_contentLbl.textColor = [UIColor blackColor];
}
}
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
[super setHighlighted:highlighted animated:animated];
NSLog(@"set cell %d highlighted: %d", indexPath.row, highlighted);
if (highlighted) {
_contentLbl.textColor = [UIColor whiteColor];
}
else {
_contentLbl.textColor = [UIColor blackColor];
}
}
下面是運行效果,左邊是正常情況下,右邊是選中狀態下:

二、UITableView的分割線
tableview自帶了分割線,而且還非常人性化的提供了設置風格和分割線的顏色,但是自帶的分割線有個缺點就是即使你的cell只有兩三個,分割線也會充滿全屏,如下圖所示:

可是實際應用中我們有時並不想讓沒有cell的地方也有分割線,這個時候我們就需要自己動手豐衣足食了。通常有兩種做法:1、通過addSubview的添加一條分割線;2、自繪分割線。大部分人估計都會addSubview的方式,下面我們就討論一下第二種通過自繪實現分割線。
首先設置tableView的separatorStyle為UITableViewCellSelectionStyleNone,即禁用tableview自帶的分割線,然后在重載cell的drawRect方法,通過Quartz 2D技術直接進行繪制,思路如下,首先繪制整個cell的背景色,然后在cell的最下面繪制分割線,代碼片段如下:
// 自繪分割線
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillRect(context, rect);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:0xE2/255.0f green:0xE2/255.0f blue:0xE2/255.0f alpha:1].CGColor);
CGContextStrokeRect(context, CGRectMake(0, rect.size.height - 1, rect.size.width, 1));
}
這樣繪制分割線也不用擔心效率問題,因為只會在cell從隱藏變成顯示是調用一次,最上面的那兩個運行截圖都是通過自繪方式實現的。
注:歡迎轉載,轉載請注明出處。同時歡迎加我qq,期待與你一起探討更多問題。

SvTestTBCellViewController.m