在IOS開發中用的最為廣泛的元件,莫過於UITableView,今天在這篇文章中會詳細介紹一下UITableView和UITableViewCell。
什麼是UITableView
UITableView有兩種形式,一種是分組的,一種是不分組的,下面來看一下樣式:
- 分組樣式(UITableViewStyleGrouped)
- 不分組樣式(UITableViewStylePlain)
UITableView中每行資料都是一個UITableViewCell,在這個控制元件中為了顯示更多的資訊,iOS已經在其內部設定好了多個子控制元件以供開發者使用。
檢視原始碼可知:
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault, // 左側顯示textLabel(不顯示detailTextLabel),imageView可選(顯示在最左邊)
UITableViewCellStyleValue1, // 左側顯示textLabel、右側顯示detailTextLabel(預設藍色),imageView可選(顯示在最左邊)
UITableViewCellStyleValue2, // 左側依次顯示textLabel(預設藍色)和detailTextLabel,imageView可選(顯示在最左邊)
UITableViewCellStyleSubtitle // 左上方顯示textLabel,左下方顯示detailTextLabel(預設灰色),imageView可選(顯示在最左邊)
};複製程式碼
當然,這些子控制元件並不一定要全部使用,具體操作時可以通過UITableViewCellStyle進行設定。
#如何實現UITableView
在實現UITableView之前,我們先建立一個資料模型,讓UITableView來進行展示:
@interface Person : NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *sex;
@property (nonatomic,copy) NSString *age;
-(Person *)initWithName:(NSString *)name andSex:(NSString *)sex andAge:(NSString *)age;
@end複製程式碼
實現:
-(Person *)initWithName:(NSString *)name andSex:(NSString *)sex andAge:(NSString *)age{
if(self=[super init]){
self.name=name;
self.age=age;
self.sex=sex;
}
return self;
}複製程式碼
然後在ViewController中新增實現,根據上文中提到過的兩種方式,我們進行分別的實現。
UITableViewStylePlain
實現一個UITableView,比如要有一個資料來源,如果需要有互動(點選)那麼還需要一個代理,即UITableViewDataSource和UITableViewDelegate。
那我們可以都在當前的ViewController中實現:
UITableViewDataSource
對於資料來源我們可以有如下實現:
//有幾個群組
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
//一個群組有幾個item(cell)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return _persons.count;
}
//根據資料來源展示介面實現
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
Person *person=_persons[indexPath.row];
UITableViewCell *cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cell.textLabel.text=[person name];
cell.detailTextLabel.text=person.sex;
return cell;
}複製程式碼
UITableViewDelegate
點選實現,我們可以實現如下的方法:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
Person *person=_persons[indexPath.row];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"title" message:person.age delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"ok", nil];
[alert show];
}複製程式碼
初始化UITableView
然後在初始化UITableView即可:
- (void)viewDidLoad {
[super viewDidLoad];
[self initData];
_tableView=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain];
_tableView.dataSource=self;
_tableView.delegate = self;
[self.view addSubview:_tableView];
}
-(void)initData{
_persons = [[NSMutableArray alloc]init];
_mans = [[NSMutableArray alloc]init];
_womans = [[NSMutableArray alloc]init];
Person *person1 = [[Person alloc]initWithName:@"jack" andSex:@"man" andAge:@"15"];
Person *person2 = [[Person alloc]initWithName:@"jim" andSex:@"man" andAge:@"15"];
Person *person3 = [[Person alloc]initWithName:@"jj" andSex:@"man" andAge:@"15"];
Person *person4 = [[Person alloc]initWithName:@"jk" andSex:@"man" andAge:@"15"];
Person *person5 = [[Person alloc]initWithName:@"jff" andSex:@"man" andAge:@"15"];
Person *person6 = [[Person alloc]initWithName:@"tom" andSex:@"man" andAge:@"15"];
NSArray *arryman = [NSArray arrayWithObjects:person1,person2,person3,person4,person5,person6,nil];
[self.mans addObjectsFromArray:arryman];
Person *person11 = [[Person alloc]initWithName:@"lily" andSex:@"women" andAge:@"15"];
Person *person21 = [[Person alloc]initWithName:@"lucy" andSex:@"women" andAge:@"15"];
Person *person31 = [[Person alloc]initWithName:@"ll" andSex:@"women" andAge:@"15"];
Person *person41 = [[Person alloc]initWithName:@"lk" andSex:@"women" andAge:@"15"];
Person *person51 = [[Person alloc]initWithName:@"lf" andSex:@"women" andAge:@"15"];
Person *person61 = [[Person alloc]initWithName:@"nancy" andSex:@"women" andAge:@"15"];
NSArray *arrywoman = [NSArray arrayWithObjects:person11,person21,person31,person41,person51,person61,nil];
[self.womans addObjectsFromArray:arrywoman];
[_persons addObjectsFromArray:_mans];
[_persons addObjectsFromArray:_womans];
}複製程式碼
UITableViewStyleGrouped
根據上面的例子我們知道,還是需要實現UITableViewDataSource和UITableViewDelegate。
UITableViewDataSource
在這種形式的UITableViewDataSource,我們需要多實現一些東西:
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 2;//假設我們有兩個群組
}
//根據section來區分是哪個群組
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if (section == 0) {
return _mans.count;
}
else {
return _womans.count;
}
}
//實現展示介面的時候也要區分群組
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
Person *person;
if(indexPath.section == 0){
person = _mans[indexPath.row];
}else{
person = _womans[indexPath.row];
}
UITableViewCell *cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cell.textLabel.text=[person name];
cell.detailTextLabel.text=person.sex;
return cell;
}
// 返回每組頭標題名稱
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
if (section == 0) {
return @"man";
}else{
return @"woman";
}
}
// 返回每組尾部說明
-(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section{
if (section == 0) {
return @"man footer";
}else{
return @"woman footer";
}
}複製程式碼
UITableViewDelegate
在UITableViewDelegate的實現中也要區分群組:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
Person *person;
if(indexPath.section == 0){
person = _mans[indexPath.row];
}else{
person = _womans[indexPath.row];
}
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"title" message:person.age delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"ok", nil];
[alert show];
}複製程式碼
初始化UITableView
這裡跟上面基本一樣,唯一的區別就是設定型別為UITableViewStyleGrouped
_tableView=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped];複製程式碼
更多功能
設定行高
在UITableViewDelegate中還有一些特殊的功能,通過一些方法來實現:
#pragma mark 設定分組標題內容高度
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
if(section==0){
return 50;
}
return 40;
}
#pragma mark 設定每行高度(每行高度可以不一樣)
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 45;
}
#pragma mark 設定尾部說明內容高度
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
return 40;
}複製程式碼
生成索引
在UITableViewDataSource中還有一些特殊的功能,通過一些方法來實現:
#pragma mark 返回每組標題索引
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{
NSLog(@"生成組索引");
NSMutableArray *indexs=[[NSMutableArray alloc]init];
[indexs addObject:@"man"];
[indexs addObject:@"woman"];
return indexs;
}複製程式碼
效能優化
UITableView中的單元格cell是在顯示到使用者可視區域後建立的,那麼如果使用者往下滾動就會繼續建立顯示在螢幕上的單元格,如果使用者向上滾動返回到檢視過的內容時同樣會重新建立之前已經建立過的單元格。如此一來即使UITableView的內容不是太多,如果使用者反覆的上下滾動,記憶體也會瞬間飆升,更何況很多時候UITableView的內容是很多的(例如微博展示列表,基本向下滾動是沒有底限的)。
做過Android的同學應該知道,在ListView中有一種機制,就是複用view來做優化,IOS的UITableView中其實已經自我實現了這種機制,也就是將當前沒有顯示的Cell重新顯示在將要顯示的Cell的位置然後更新其內容。
在UITableView內部有一個快取池,初始化時使用initWithStyle:(UITableViewCellStyle) reuseIdentifier:(NSString *)方法指定一個可重用標識,就可以將這個cell放到快取池。然後在使用時使用指定的標識去快取池中取得對應的cell然後修改cell內容即可。
#pragma mark返回每行的單元格
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
Person *person;
if(indexPath.section == 0){
person = _mans[indexPath.row];
}else{
person = _womans[indexPath.row];
}
//由於此方法呼叫十分頻繁,cell的標示宣告成靜態變數有利於效能優化
static NSString *cellIdentifier=@"UITableViewCellIdentifierKey1";
//首先根據標識去快取池取
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(!cell){
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
}
cell.textLabel.text=[person name];
cell.detailTextLabel.text=person.sex;
return cell;
}複製程式碼
UITableViewCell
原生的UITableViewCell
UITableViewCell是構建一個UITableView的基礎,在UITableViewCell內部有一個UIView控制元件作為其他內容的容器,它上面有一個UIImageView和兩個UILabel,通過UITableViewCellStyle屬性可以對其樣式進行控制。
typedef NS_ENUM(NSInteger, UITableViewCellAccessoryType) {
UITableViewCellAccessoryNone, // 不顯示任何圖示
UITableViewCellAccessoryDisclosureIndicator, // 跳轉指示圖示
UITableViewCellAccessoryDetailDisclosureButton, // 內容詳情圖示和跳轉指示圖示
UITableViewCellAccessoryCheckmark, // 勾選圖示
UITableViewCellAccessoryDetailButton NS_ENUM_AVAILABLE_IOS(7_0) // 內容詳情圖示
};複製程式碼
新增自定義元件
如果上述的屬性我都不想用,比如我想用一個UISwitch控制元件,應該如何進行設定呢?
繼續修改一下-(UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
Person *person;
if(indexPath.section == 0){
person = _mans[indexPath.row];
}else{
person = _womans[indexPath.row];
}
//由於此方法呼叫十分頻繁,cell的標示宣告成靜態變數有利於效能優化
static NSString *cellIdentifier=@"UITableViewCellIdentifierKey1";
static NSString *cellIdentifierForFirstRow=@"UITableViewCellIdentifierKeyWithSwitch";
//首先根據標識去快取池取
UITableViewCell *cell;
if (indexPath.row==0) {
cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifierForFirstRow];
}else{
cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
}
if(!cell){
if (indexPath.row==0) {
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifierForFirstRow];
UISwitch *sw=[[UISwitch alloc]init];
[sw addTarget:self action:@selector(switchValueChange:) forControlEvents:UIControlEventValueChanged];
cell.accessoryView=sw;
}else{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
cell.accessoryType=UITableViewCellAccessoryDetailButton;
}
}
if(indexPath.row==0){
((UISwitch *)cell.accessoryView).tag=indexPath.section;
}
cell.textLabel.text=[person name];
cell.detailTextLabel.text=person.sex;
return cell;
}
#pragma mark 切換開關轉化事件
-(void)switchValueChange:(UISwitch *)sw{
NSLog(@"section:%li,switch:%i",(long)sw.tag, sw.on);
}複製程式碼
程式碼表示我們在第一行新增了一個開關的控制元件。
效果如圖:
自定義UITableViewCell
假如系統提供的UITableViewCell滿足不了我們的需要,我們可以進行自定義,新建一個CustomCellTableViewCell繼承UITableViewCell
#import <UIKit/UIKit.h>
#import "Person.h"
@interface CustomCellTableViewCell : UITableViewCell
@property (nonatomic,strong) Person *person;
@property (assign,nonatomic) CGFloat height;
@end複製程式碼
實現:
@interface CustomCellTableViewCell(){
UILabel *_text1;
UILabel *_text2;
UILabel *_text3;
}
@end
@implementation CustomCellTableViewCell
- (void)awakeFromNib {
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self initSubView];
}
return self;
}
#pragma mark 初始化檢視
-(void)initSubView{
_text1=[[UILabel alloc]init];
_text1.textColor=[UIColor redColor];
[_text1 setFrame:CGRectMake(0, 0, 40, 20)];
[self.contentView addSubview:_text1];
_text2=[[UILabel alloc]init];
_text2.textColor=[UIColor yellowColor];
[_text2 setFrame:CGRectMake(40, 0, 40, 20)];
[self.contentView addSubview:_text2];
_text3=[[UILabel alloc]init];
_text3.textColor=[UIColor blueColor];
[_text3 setFrame:CGRectMake(80, 0, 40, 20)];
[self.contentView addSubview:_text3];
}
#pragma mark 設定
-(void)setPerson:(Person *)person{
[_text1 setText:person.name];
[_text2 setText:person.sex];
[_text3 setText:person.age];
}
@end複製程式碼
然後修改一下使用的ViewController
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIdentifier=@"UITableViewCellIdentifierKey1";
CustomCellTableViewCell *cell;
cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(!cell){
cell=[[CustomCellTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
Person *person;
if(indexPath.section == 0){
person = _mans[indexPath.row];
}else{
person = _womans[indexPath.row];
}
cell.person=person;
return cell;
}複製程式碼
效果如下圖所示:
UITableViewController
很多時候一個UIViewController中只有一個UITableView,因此蘋果官方為了方便大家開發直接提供了一個UITableViewController,這個控制器 UITableViewController實現了UITableView資料來源和代理協議,內部定義了一個tableView屬性供外部訪問,同時自動鋪滿整個螢幕、自動伸縮以方便我們的開發。
實現起來跟前面提到的都一樣,所以,這裡不再贅述。
總結
UITableView確實給我們提供了一些很強大的功能和展示效果,用好用對UITableView對於IOS程式開發是非常有必要的。