在IOS開發中用的最爲普遍的組件,莫過於UITableView,今天在這篇文章中會詳細介紹一下UITableView和UITableViewCell。javascript
UITableView有兩種形式,一種是分組的,一種是不分組的,下面來看一下樣式:java
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中添加實現,根據上文中提到過的兩種方式,咱們進行分別的實現。ui
實現一個UITableView,好比要有一個數據源,若是須要有交互(點擊)那麼還須要一個代理,即UITableViewDataSource和UITableViewDelegate。atom
那咱們能夠都在當前的ViewController中實現:spa
對於數據源咱們能夠有以下實現:代理
//有幾個羣組
-(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;
}複製代碼
點擊實現,咱們能夠實現以下的方法:
- (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便可:
- (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];
}複製代碼
根據上面的例子咱們知道,仍是須要實現UITableViewDataSource和UITableViewDelegate。
在這種形式的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的實現中也要區分羣組:
- (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];
}複製代碼
這裏跟上面基本同樣,惟一的區別就是設置類型爲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是構建一個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知足不了咱們的須要,咱們能夠進行自定義,新建一個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;
}複製代碼
效果以下圖所示:
不少時候一個UIViewController中只有一個UITableView,所以蘋果官方爲了方便你們開發直接提供了一個UITableViewController,這個控制器 UITableViewController實現了UITableView數據源和代理協議,內部定義了一個tableView屬性供外部訪問,同時自動鋪滿整個屏幕、自動伸縮以方便咱們的開發。
實現起來跟前面提到的都同樣,因此,這裏再也不贅述。
UITableView確實給咱們提供了一些很強大的功能和展現效果,用好用對UITableView對於IOS程序開發是很是有必要的。