UITableView全面解析

Posted 无梦为安

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UITableView全面解析相关的知识,希望对你有一定的参考价值。

 

本文转自:http://www.cocoachina.com/ios/20140922/9710.html

 

 

在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似于微信、QQ、新浪微博等软件基本上随处都是UITableView。当然它的广泛使用自然离不开它强大的功能,今天这篇文章将针对UITableView重点展开讨论。今天的主要内容包括:

1.基本介绍
2.数据源
3.代理
4.性能优化
5.UITableViewCell
6.常用操作
7.UITableViewController
8.MVC模式
 
基本介绍
UITableView有两种风格:UITableViewStylePlain和UITableViewStyleGrouped。这两者操作起来其实并没有本质区别,只是后者按分组样式显示前者按照普通样式显示而已。大家先看一下两者的应用:
 
1>分组样式
技术分享
2>不分组样式
技术分享
 
大家可以看到在UITableView中数据只有行的概念,并没有列的概念,因为在手机操作系统中显示多列是不利于操作的。UITableView中每行数据都是一个UITableViewCell,在这个控件中为了显示更多的信息,iOS已经在其内部设置好了多个子控件以供开发者使用。如果我们查看UITableViewCell的声明文件可以发现在内部有一个UIView控件(contentView,作为其他元素的父控件)、两个UILable控件(textLabel、detailTextLabel)、一个UIImage控件(imageView),分别用于容器、显示内容、详情和图片。使用效果类似于微信、QQ信息列表:
技术分享
当然,这些子控件并不一定要全部使用,具体操作时可以通过UITableViewCellStyle进行设置,具体每个枚举表示的意思已经在代码中进行了注释:
  1. typedef NS_ENUM(NSInteger, UITableViewCellStyle) { 
  2.     UITableViewCellStyleDefault,    // 左侧显示textLabel(不显示detailTextLabel),imageView可选(显示在最左边) 
  3.     UITableViewCellStyleValue1,        // 左侧显示textLabel、右侧显示detailTextLabel(默认蓝色),imageView可选(显示在最左边) 
  4.     UITableViewCellStyleValue2,        // 左侧依次显示textLabel(默认蓝色)和detailTextLabel,imageView可选(显示在最左边) 
  5.     UITableViewCellStyleSubtitle    // 左上方显示textLabel,左下方显示detailTextLabel(默认灰色),imageView可选(显示在最左边) 
  6. }; 
数据源
由于iOS是遵循MVC模式设计的,很多操作都是通过代理和外界沟通的,但对于数据源控件除了代理还有一个数据源属性,通过它和外界进行数据交互。 对于UITableView设置完dataSource后需要实现UITableViewDataSource协议,在这个协议中定义了多种 数据操作方法,下面通过创建一个简单的联系人管理进行演示:
首先我们需要创建一个联系人模型KCContact
KCContact.h
  1. // 
  2. //  Contact.h 
  3. //  UITableView 
  4. // 
  5. //  Created by Kenshin Cui on 14-3-1. 
  6. //  Copyright (c) 2014年 Kenshin Cui. All rights reserved. 
  7. // 
  8.  
  9. #import  
  10.  
  11. @interface KCContact : NSObject 
  12.  
  13. #pragma mark 姓 
  14. @property (nonatomic,copy) NSString *firstName; 
  15. #pragma mark 名 
  16. @property (nonatomic,copy) NSString *lastName; 
  17. #pragma mark 手机号码 
  18. @property (nonatomic,copy) NSString *phoneNumber; 
  19.  
  20. #pragma mark 带参数的构造函数 
  21. -(KCContact *)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName andPhoneNumber:(NSString *)phoneNumber; 
  22.  
  23. #pragma mark 取得姓名 
  24. -(NSString *)getName; 
  25.  
  26.  
  27. #pragma mark 带参数的静态对象初始化方法 
  28. +(KCContact *)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName andPhoneNumber:(NSString *)phoneNumber; 
  29. @end 
KCContact.m
  1. // 
  2. //  Contact.m 
  3. //  UITableView 
  4. // 
  5. //  Created by Kenshin Cui on 14-3-1. 
  6. //  Copyright (c) 2014年 Kenshin Cui. All rights reserved. 
  7. // 
  8.  
  9. #import "KCContact.h" 
  10.  
  11. @implementation KCContact 
  12.  
  13. -(KCContact *)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName andPhoneNumber:(NSString *)phoneNumber{ 
  14.     if(self=[super init]){ 
  15.         self.firstName=firstName; 
  16.         self.lastName=lastName; 
  17.         self.phoneNumber=phoneNumber; 
  18.     } 
  19.     return self; 
  20.  
  21. -(NSString *)getName{ 
  22.     return [NSString stringWithFormat:@"%@ %@",_lastName,_firstName]; 
  23.  
  24. +(KCContact *)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName andPhoneNumber:(NSString *)phoneNumber{ 
  25.     KCContact *contact1=[[KCContact alloc]initWithFirstName:firstName andLastName:lastName andPhoneNumber:phoneNumber]; 
  26.     return contact1; 
  27.  
  28. @end 
为了演示分组显示我们不妨将一组数据也抽象成模型KCContactGroup
KCContactGroup.h
  1. // 
  2. //  KCContactGroup.h 
  3. //  UITableView 
  4. // 
  5. //  Created by Kenshin Cui on 14-3-1. 
  6. //  Copyright (c) 2014年 Kenshin Cui. All rights reserved. 
  7. // 
  8.  
  9. #import  
  10. #import "KCContact.h" 
  11.  
  12. @interface KCContactGroup : NSObject 
  13.  
  14. #pragma mark 组名 
  15. @property (nonatomic,copy) NSString *name; 
  16.  
  17. #pragma mark 分组描述 
  18. @property (nonatomic,copy) NSString *detail; 
  19.  
  20. #pragma mark 联系人 
  21. @property (nonatomic,strong) NSMutableArray *contacts; 
  22.  
  23. #pragma mark 带参数个构造函数 
  24. -(KCContactGroup *)initWithName:(NSString *)name andDetail:(NSString *)detail andContacts:(NSMutableArray *)contacts; 
  25.  
  26. #pragma mark 静态初始化方法 
  27. +(KCContactGroup *)initWithName:(NSString *)name andDetail:(NSString *)detail andContacts:(NSMutableArray *)contacts; 
  28.  
  29. @end 
KCContactGroup.m
  1. // 
  2. //  KCContactGroup.m 
  3. //  UITableView 
  4. // 
  5. //  Created by Kenshin Cui on 14-3-1. 
  6. //  Copyright (c) 2014年 Kenshin Cui. All rights reserved. 
  7. // 
  8.  
  9. #import "KCContactGroup.h" 
  10.  
  11. @implementation KCContactGroup 
  12.  
  13.  
  14. -(KCContactGroup *)initWithName:(NSString *)name andDetail:(NSString *)detail andContacts:(NSMutableArray *)contacts{ 
  15.     if (self=[super init]) { 
  16.         self.name=name; 
  17.         self.detail=detail; 
  18.         self.contacts=contacts; 
  19.     } 
  20.     return self; 
  21.  
  22. +(KCContactGroup *)initWithName:(NSString *)name andDetail:(NSString *)detail andContacts:(NSMutableArray *)contacts{ 
  23.     KCContactGroup *group1=[[KCContactGroup alloc]initWithName:name andDetail:detail andContacts:contacts]; 
  24.     return group1; 
  25. @end 
然后在viewDidLoad方法中创建一些模拟数据同时实现数据源协议方法:
KCMainViewController.m
  1. // 
  2. //  KCMainViewController.m 
  3. //  UITableView 
  4. // 
  5. //  Created by Kenshin Cui on 14-3-1. 
  6. //  Copyright (c) 2014年 Kenshin Cui. All rights reserved. 
  7. // 
  8.  
  9. #import "KCMainViewController.h" 
  10. #import "KCContact.h" 
  11. #import "KCContactGroup.h" 
  12.  
  13. @interface KCMainViewController (){ 
  14.     UITableView *_tableView; 
  15.     NSMutableArray *_contacts;//联系人模型 
  16.  
  17. @end 
  18.  
  19. @implementation KCMainViewController 
  20.  
  21. - (void)viewDidLoad { 
  22.     [super viewDidLoad]; 
  23.      
  24.     //初始化数据 
  25.     [self initData]; 
  26.      
  27.     //创建一个分组样式的UITableView 
  28.     _tableView=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; 
  29.      
  30.     //设置数据源,注意必须实现对应的UITableViewDataSource协议 
  31.     _tableView.dataSource=self; 
  32.      
  33.     [self.view addSubview:_tableView]; 
  34.  
  35. #pragma mark 加载数据 
  36. -(void)initData{ 
  37.     _contacts=[[NSMutableArray alloc]init]; 
  38.      
  39.     KCContact *contact1=[KCContact initWithFirstName:@"Cui" andLastName:@"Kenshin" andPhoneNumber:@"18500131234"]; 
  40.     KCContact *contact2=[KCContact initWithFirstName:@"Cui" andLastName:@"Tom" andPhoneNumber:@"18500131237"]; 
  41.     KCContactGroup *group1=[KCContactGroup initWithName:@"C" andDetail:@"With names beginning with C" andContacts:[NSMutableArray arrayWithObjects:contact1,contact2, nil]]; 
  42.     [_contacts addObject:group1]; 
  43.      
  44.  
  45.      
  46.     KCContact *contact3=[KCContact initWithFirstName:@"Lee" andLastName:@"Terry" andPhoneNumber:@"18500131238"]; 
  47.     KCContact *contact4=[KCContact initWithFirstName:@"Lee" andLastName:@"Jack" andPhoneNumber:@"18500131239"]; 
  48.     KCContact *contact5=[KCContact initWithFirstName:@"Lee" andLastName:@"Rose" andPhoneNumber:@"18500131240"]; 
  49.     KCContactGroup *group2=[KCContactGroup initWithName:@"L" andDetail:@"With names beginning with L" andContacts:[NSMutableArray arrayWithObjects:contact3,contact4,contact5, nil]]; 
  50.     [_contacts addObject:group2]; 
  51.      
  52.      
  53.      
  54.     KCContact *contact6=[KCContact initWithFirstName:@"Sun" andLastName:@"Kaoru" andPhoneNumber:@"18500131235"]; 
  55.     KCContact *contact7=[KCContact initWithFirstName:@"Sun" andLastName:@"Rosa" andPhoneNumber:@"18500131236"]; 
  56.  
  57.     KCContactGroup *group3=[KCContactGroup initWithName:@"S" andDetail:@"With names beginning with S" andContacts:[NSMutableArray arrayWithObjects:contact6,contact7, nil]]; 
  58.     [_contacts addObject:group3]; 
  59.      
  60.      
  61.     KCContact *contact8=[KCContact initWithFirstName:@"Wang" andLastName:@"Stephone" andPhoneNumber:@"18500131241"]; 
  62.     KCContact *contact9=[KCContact initWithFirstName:@"Wang" andLastName:@"Lucy" andPhoneNumber:@"18500131242"]; 
  63.     KCContact *contact10=[KCContact initWithFirstName:@"Wang" andLastName:@"Lily" andPhoneNumber:@"18500131243"]; 
  64.     KCContact *contact11=[KCContact initWithFirstName:@"Wang" andLastName:@"Emily" andPhoneNumber:@"18500131244"]; 
  65.     KCContact *contact12=[KCContact initWithFirstName:@"Wang" andLastName:@"Andy" andPhoneNumber:@"18500131245"]; 
  66.     KCContactGroup *group4=[KCContactGroup initWithName:@"W" andDetail:@"With names beginning with W" andContacts:[NSMutableArray arrayWithObjects:contact8,contact9,contact10,contact11,contact12, nil]]; 
  67.     [_contacts addObject:group4]; 
  68.      
  69.      
  70.     KCContact *contact13=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joy" andPhoneNumber:@"18500131246"]; 
  71.     KCContact *contact14=[KCContact initWithFirstName:@"Zhang" andLastName:@"Vivan" andPhoneNumber:@"18500131247"]; 
  72.     KCContact *contact15=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joyse" andPhoneNumber:@"18500131248"]; 
  73.     KCContactGroup *group5=[KCContactGroup initWithName:@"Z" andDetail:@"With names beginning with Z" andContacts:[NSMutableArray arrayWithObjects:contact13,contact14,contact15, nil]]; 
  74.     [_contacts addObject:group5]; 
  75.  
  76.  
  77. #pragma mark - 数据源方法 
  78. #pragma mark 返回分组数 
  79. -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ 
  80.     NSLog(@"计算分组数"); 
  81.     return _contacts.count; 
  82.  
  83. #pragma mark 返回每组行数 
  84. -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 
  85.     NSLog(@"计算每组(组%i)行数",section); 
  86.     KCContactGroup *group1=_contacts[section]; 
  87.     return group1.contacts.count; 
  88.  
  89. #pragma mark返回每行的单元格 
  90. -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 
  91.     //NSIndexPath是一个结构体,记录了组和行信息 
  92.     NSLog(@"生成单元格(组:%i,行%i)",indexPath.section,indexPath.row); 
  93.     KCContactGroup *group=_contacts[indexPath.section]; 
  94.     KCContact *contact=group.contacts[indexPath.row]; 
  95.     UITableViewCell *cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil]; 
  96.     cell.textLabel.text=[contact getName]; 
  97.     cell.detailTextLabel.text=contact.phoneNumber; 
  98.     return cell; 
  99.  
  100. #pragma mark 返回每组头标题名称 
  101. -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ 
  102.     NSLog(@"生成组(组%i)名称",section); 
  103.     KCContactGroup *group=_contacts[section]; 
  104.     return group.name; 
  105.  
  106. #pragma mark 返回每组尾部说明 
  107. -(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section{ 
  108.     NSLog(@"生成尾部(组%i)详情",section); 
  109.     KCContactGroup *group=_contacts[section]; 
  110.     return group.detail; 
  111. @end 
运行可以看到如下效果:
技术分享
大家在使用iPhone通讯录时会发现右侧可以按字母检索,使用起来很方便,其实这个功能使用UITableView实现很简单,只要实现数据源协议的一个方法,构建一个分组标题的数组即可实现。数组元素的内容和组标题内容未必完全一致,UITableView是按照数组元素的索引和每组数据索引顺序来定位的而不是按内容查找。
  1. #pragma mark 返回每组标题索引 
  2. -(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{ 
  3.     NSLog(@"生成组索引"); 
  4.     NSMutableArray *indexs=[[NSMutableArray alloc]init]; 
  5.     for(KCContactGroup *group in _contacts){ 
  6.         [indexs addObject:group.name]; 
  7.     } 
  8.     return indexs; 
效果如下:
技术分享
需要注意的是上面几个重点方法的执行顺序,请看下图:
技术分享
值得指出的是生成单元格的方法并不是一次全部调用,而是只会生产当前显示在界面上的单元格,当用户滚动操作时再显示其他单元格。
 
注意:随着我们的应用越来越复杂,可能经常需要调试程序,在iOS中默认情况下不能定位到错误代码行,我们可以通过如下设置让程序定位到出错代码行:Show the Breakpoint  navigator—Add Exception breakpoint。
 
代理
上面我们已经看到通讯录的简单实现,但是我们发现单元格高度、分组标题高度以及尾部说明的高度都需要调整,此时就需要使用代理方法。UITableView代理方法有很多,例如监听单元格显示周期、监听单元格选择编辑操作、设置是否高亮显示单元格、设置行高等。
1.设置行高
  1. #pragma mark - 代理方法 
  2. #pragma mark 设置分组标题内容高度 
  3. -(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{ 
  4.     if(section==0){ 
  5.         return 50; 
  6.     } 
  7.     return 40; 
  8.  
  9. #pragma mark 设置每行高度(每行高度可以不一样) 
  10. -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 
  11.     return 45; 
  12.  
  13. #pragma mark 设置尾部说明内容高度 
  14. -(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{ 
  15.     return 40; 
2.监听点击
在iOS中点击某联系个人就可以呼叫这个联系人,这时就需要监听点击操作,这里就不演示呼叫联系人操作了,我们演示一下修改人员信息的操作。
KCMainViewContrller.m
  1. // 
  2. //  KCMainViewController.m 
  3. //  UITableView 
  4. // 
  5. //  Created by Kenshin Cui on 14-3-1. 
  6. //  Copyright (c) 2014年 Kenshin Cui. All rights reserved. 
  7. // 
  8.  
  9. #import "KCMainViewController.h" 
  10. #import "KCContact.h" 
  11. #import "KCContactGroup.h" 
  12.  
  13. @interface KCMainViewController ()<uitableviewdatasource,uitableviewdelegate,uialertviewdelegate>{ 
  14.     UITableView *_tableView; 
  15.     NSMutableArray *_contacts;//联系人模型 
  16.     NSIndexPath *_selectedIndexPath;//当前选中的组和行 
  17.  
  18. @end 
  19.  
  20. @implementation KCMainViewController 
  21.  
  22. - (void)viewDidLoad { 
  23.     [super viewDidLoad]; 
  24.      
  25.     //初始化数据 
  26.     [self initData]; 
  27.      
  28.     //创建一个分组样式的UITableView 
  29.     _tableView=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; 
  30.      
  31.     //设置数据源,注意必须实现对应的UITableViewDataSource协议 
  32.     _tableView.dataSource=self; 
  33.     //设置代理 
  34.     _tableView.delegate=self; 
  35.      
  36.     [self.view addSubview:_tableView]; 
  37.  
  38. #pragma mark 加载数据 
  39. -(void)initData{ 
  40.     _contacts=[[NSMutableArray alloc]init]; 
  41.      
  42.     KCContact *contact1=[KCContact initWithFirstName:@"Cui" andLastName:@"Kenshin" andPhoneNumber:@"18500131234"]; 
  43.     KCContact *contact2=[KCContact initWithFirstName:@"Cui" andLastName:@"Tom" andPhoneNumber:@"18500131237"]; 
  44.     KCContactGroup *group1=[KCContactGroup initWithName:@"C" andDetail:@"With names beginning with C" andContacts:[NSMutableArray arrayWithObjects:contact1,contact2, nil]]; 
  45.     [_contacts addObject:group1]; 
  46.      
  47.  
  48.      
  49.     KCContact *contact3=[KCContact initWithFirstName:@"Lee" andLastName:@"Terry" andPhoneNumber:@"18500131238"]; 
  50.     KCContact *contact4=[KCContact initWithFirstName:@"Lee" andLastName:@"Jack" andPhoneNumber:@"18500131239"]; 
  51.     KCContact *contact5=[KCContact initWithFirstName:@"Lee" andLastName:@"Rose" andPhoneNumber:@"18500131240"]; 
  52.     KCContactGroup *group2=[KCContactGroup initWithName:@"L" andDetail:@"With names beginning with L" andContacts:[NSMutableArray arrayWithObjects:contact3,contact4,contact5, nil]]; 
  53.     [_contacts addObject:group2]; 
  54.      
  55.      
  56.      
  57.     KCContact *contact6=[KCContact initWithFirstName:@"Sun" andLastName:@"Kaoru" andPhoneNumber:@"18500131235"]; 
  58.     KCContact *contact7=[KCContact initWithFirstName:@"Sun" andLastName:@"Rosa" andPhoneNumber:@"18500131236"]; 
  59.  
  60.     KCContactGroup *group3=[KCContactGroup initWithName:@"S" andDetail:@"With names beginning with S" andContacts:[NSMutableArray arrayWithObjects:contact6,contact7, nil]]; 
  61.     [_contacts addObject:group3]; 
  62.      
  63.      
  64.     KCContact *contact8=[KCContact initWithFirstName:@"Wang" andLastName:@"Stephone" andPhoneNumber:@"18500131241"]; 
  65.     KCContact *contact9=[KCContact initWithFirstName:@"Wang" andLastName:@"Lucy" andPhoneNumber:@"18500131242"]; 
  66.     KCContact *contact10=[KCContact initWithFirstName:@"Wang" andLastName:@"Lily" andPhoneNumber:@"18500131243"]; 
  67.     KCContact *contact11=[KCContact initWithFirstName:@"Wang" andLastName:@"Emily" andPhoneNumber:@"18500131244"]; 
  68.     KCContact *contact12=[KCContact initWithFirstName:@"Wang" andLastName:@"Andy" andPhoneNumber:@"18500131245"]; 
  69.     KCContactGroup *group4=[KCContactGroup initWithName:@"W" andDetail:@"With names beginning with W" andContacts:[NSMutableArray arrayWithObjects:contact8,contact9,contact10,contact11,contact12, nil]]; 
  70.     [_contacts addObject:group4]; 
  71.      
  72.      
  73.     KCContact *contact13=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joy" andPhoneNumber:@"18500131246"]; 
  74.     KCContact *contact14=[KCContact initWithFirstName:@"Zhang" andLastName:@"Vivan" andPhoneNumber:@"18500131247"]; 
  75.     KCContact *contact15=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joyse" andPhoneNumber:@"18500131248"]; 
  76.     KCContactGroup *group5=[KCContactGroup initWithName:@"Z" andDetail:@"With names beginning with Z" andContacts:[NSMutableArray arrayWithObjects:contact13,contact14,contact15, nil]]; 
  77.     [_contacts addObject:group5]; 
  78.  
  79.  
  80. #pragma mark - 数据源方法 
  81. #pragma mark 返回分组数 
  82. -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ 
  83.     NSLog(@"计算分组数"); 
  84.     return _contacts.count; 
  85.  
  86. #pragma mark 返回每组行数 
  87. -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 
  88.     NSLog(@"计算每组(组%i)行数",section); 
  89.     KCContactGroup *group1=_contacts[section]; 
  90.     return group1.contacts.count; 
  91.  
  92. #pragma mark返回每行的单元格 
  93. -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 
  94.     //NSIndexPath是一个对象,记录了组和行信息 
  95.     NSLog(@"生成单元格(组:%i,行%i)",indexPath.section,indexPath.row); 
  96.     KCContactGroup *group=_contacts[indexPath.section]; 
  97.     KCContact *contact=group.contacts[indexPath.row]; 
  98.     UITableViewCell *cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil]; 
  99.     cell.textLabel.text=[contact getName]; 
  100.     cell.detailTextLabel.text=contact.phoneNumber; 
  101.     return cell; 
  102.  
  103. #pragma mark 返回每组头标题名称 
  104. -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ 
  105.     NSLog(@"生成组(组%i)名称",section); 
  106.     KCContactGroup *group=_contacts[section]; 
  107.     return group.name; 
  108.  
  109. #pragma mark 返回每组尾部说明 
  110. -(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section{ 
  111.     NSLog(@"生成尾部(组%i)详情",section); 
  112.     KCContactGroup *group=_contacts[section]; 
  113.     return group.detail; 
  114.  
  115. #pragma mark 返回每组标题索引 
  116. -(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{ 
  117.     NSLog(@"生成组索引"); 
  118.     NSMutableArray *indexs=[[NSMutableArray alloc]init]; 
  119.     for(KCContactGroup *group in _contacts){ 
  120.         [indexs addObject:group.name]; 
  121.     } 
  122.     return indexs; 
  123.  
  124. #pragma mark - 代理方法 
  125. #pragma mark 设置分组标题内容高度 
  126. -(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{ 
  127.     if(section==0){ 
  128.         return 50; 
  129.     } 
  130.     return 40; 
  131.  
  132. #pragma mark 设置每行高度(每行高度可以不一样) 
  133. -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 
  134.     return 45; 
  135.  
  136. #pragma mark 设置尾部说明内容高度 
  137. -(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{ 
  138.     return 40; 
  139.  
  140. #pragma mark 点击行 
  141. -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ 
  142.     _selectedIndexPath=indexPath; 
  143.     KCContactGroup *group=_contacts[indexPath.section]; 
  144.     KCContact *contact=group.contacts[indexPath.row]; 
  145.     //创建弹出窗口 
  146.     UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"System Info" message:[contact getName] delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil]; 
  147.     alert.alertViewStyle=UIAlertViewStylePlainTextInput; //设置窗口内容样式 
  148.     UITextField *textField= [alert textFieldAtIndex:0]; //取得文本框 
  149.     textField.text=contact.phoneNumber; //设置文本框内容 
  150.     [alert show]; //显示窗口 
  151.  
  152. #pragma mark 窗口的代理方法,用户保存数据 
  153. -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ 
  154.     //当点击了第二个按钮(OK) 
  155.     if (buttonIndex==1) { 
  156.         UITextField *textField= [alertView textFieldAtIndex:0]; 
  157.         //修改模型数据 
  158.         KCContactGroup *group=_contacts[_selectedIndexPath.section]; 
  159.         KCContact *contact=group.contacts[_selectedIndexPath.row]; 
  160.         contact.phoneNumber=textField.text; 
  161.         //刷新表格 
  162.         [_tableView reloadData]; 
  163.     } 
  164.  
  165. #pragma mark 重写状态样式方法 
  166. -(UIStatusBarStyle)preferredStatusBarStyle{ 
  167.     return UIStatusBarStyleLightContent; 
  168. @end 
在上面的代码中我们通过修改模型来改变UI显示,这种方式是经典的MVC应用,在后面的代码中会经常看到。当然UI的刷新使用了UITableView的reloadData方法,该方法会重新调用数据源方法,包括计算分组、计算每个分组的行数,生成单元格等刷新整个UITableView。当然这种方式在实际开发中是不可取的,我们不可能因为修改了一个人的信息就刷新整个UITableViewView,此时我们需要采用局部刷新。局部刷新使用起来很简单,只需要调用UITableView的另外一个方法:
  1. #pragma mark 窗口的代理方法,用户保存数据 
  2. -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ 
  3.     //当点击了第二个按钮(OK) 
  4.     if (buttonIndex==1) { 
  5.         UITextField *textField= [alertView textFieldAtIndex:0]; 
  6.         //修改模型数据 
  7.         KCContactGroup *group=_contacts[_selectedIndexPath.section]; 
  8.         KCContact *contact=group.contacts[_selectedIndexPath.row]; 
  9.         contact.phoneNumber=textField.text; 
  10.         //刷新表格 
  11.         NSArray *[email protected][_selectedIndexPath];//需要局部刷新的单元格的组、行 
  12.         [_tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationLeft];//后面的参数代表更新时的动画 
  13.     } 
性能优化
前面已经说过UITableView中的单元格cell是在显示到用户可视区域后创建的,那么如果用户往下滚动就会继续创建显示在屏幕上的单元格,如果用户向上滚动返回到查看过的内容时同样会重新创建之前已经创建过的单元格。如此一来即使UITableView的内容不是太多,如果用户反复的上下滚动,内存也会瞬间飙升,更何况很多时候UITableView的内容是很多的(例如微博展示列表,基本向下滚动是没有底限的)。
 
前面一节中我们曾经提到过如何优化UIScrollView,当时就是利用有限的UIImageView动态切换其内容来尽可能减少资源占用。同样的,在UITableView中也可以采用类似的方式,只是这时我们不是在滚动到指定位置后更改滚动的位置而是要将当前没有显示的Cell重新显示在将要显示的Cell的位置然后更新其内容。原因就是UITableView中的Cell结构布局可能是不同的,通过重新定位是不可取的,而是需要重用已经不再界面显示的已创建过的Cell。
 
当然,听起来这么做比较复杂,其实实现起来很简单,因为UITableView已经为我们实现了这种机制。在UITableView内部有一个缓存池,初始化时使用initWithStyle:(UITableViewCellStyle) reuseIdentifier:(NSString *)方法指定一个可重用标识,就可以将这个cell放到缓存池。然后在使用时使用指定的标识去缓存池中取得对应的cell然后修改cell内容即可。
 
上面的代码中已经打印了cell的地址,如果大家运行测试上下滚动UITableView会发现滚动时创建的cell地址是初始化时已经创建的。
 
这里再次给大家强调两点:
1. -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)方法调用很频繁,无论是初始化、上下滚动、刷新都会调用此方法,所有在这里执行的操作一定要注意性能;
2. 可重用标识可以有多个,如果在UITableView中有多类结构不同的Cell,可以通过这个标识进行缓存和重新。
 
UITableViewCell
1.自带的UITableViewCell
UITableViewCell是构建一个UITableView的基础,在UITableViewCell内部有一个UIView控件作为其他内容的容器,它上面有一个UIImageView和两个UILabel,通过UITableViewCellStyle属性可以对其样式进行控制。其结构如下:
技术分享
有时候我们会发现很多UITableViewCell右侧可以显示不同的图标,在iOS中称之为访问器,点击可以触发不同的事件,例如设置功能:
技术分享
要设置这些图标只需要设置UITableViewCell的accesoryType属性,这是一个枚举类型具体含义如下:
  1. typedef NS_ENUM(NSInteger, UITableViewCellAccessoryType) { 
  2.     UITableViewCellAccessoryNone,                   // 不显示任何图标 
  3.     UITableViewCellAccessoryDisclosureIndicator,    // 跳转指示图标 
  4.     UITableViewCellAccessoryDetailDisclosureButton, // 内容详情图标和跳转指示图标 
  5.     UITableViewCellAccessoryCheckmark,              // 勾选图标 
  6.     UITableViewCellAccessoryDetailButton NS_ENUM_AVAILABLE_IOS(7_0) // 内容详情图标 
  7. }; 
例如在最近通话中我们通常设置为详情图标,点击可以查看联系人详情:
技术分享
很明显iOS设置中第一个accessoryType不在枚举之列,右侧的访问器类型是UISwitch控件,那么如何显示自定义的访问器呢?其实只要设置UITableViewCell的accessoryView即可,它支持任何UIView控件。假设我们在通讯录每组第一行放一个UISwitch,同时切换时可以输出对应信息:
  1. // 
  2. //  KCMainViewController.m 
  3. //  UITableView 
  4. // 
  5. //  Created by Kenshin Cui on 14-3-1. 
  6. //  Copyright (c) 2014年 Kenshin Cui. All rights reserved. 
  7. // 
  8.  
  9. #import "KCMainViewController.h" 
  10. #import "KCContact.h" 
  11. #import "KCContactGroup.h" 
  12.  
  13. @interface KCMainViewController ()<uitableviewdatasource,uitableviewdelegate,uialertviewdelegate>{ 
  14.     UITableView *_tableView; 
  15.     NSMutableArray *_contacts;//联系人模型 
  16.     NSIndexPath *_selectedIndexPath;//当前选中的组和行 
  17.  
  18. @end 
  19.  
  20. @implementation KCMainViewController 
  21.  
  22. - (void)viewDidLoad { 
  23.     [super viewDidLoad]; 
  24.      
  25.     //初始化数据 
  26.     [self initData]; 
  27.      
  28.     //创建一个分组样式的UITableView 
  29.     _tableView=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; 
  30.      
  31.     //设置数据源,注意必须实现对应的UITableViewDataSource协议 
  32.     _tableView.dataSource=self; 
  33.     //设置代理 
  34.     _tableView.delegate=self; 
  35.      
  36.     [self.view addSubview:_tableView]; 
  37.  
  38. #pragma mark 加载数据 
  39. -(void)initData{ 
  40.     _contacts=[[NSMutableArray alloc]init]; 
  41.      
  42.     KCContact *contact1=[KCContact initWithFirstName:@"Cui" andLastName:@"Kenshin" andPhoneNumber:@"18500131234"]; 
  43.     KCContact *contact2=[KCContact initWithFirstName:@"Cui" andLastName:@"Tom" andPhoneNumber:@"18500131237"]; 
  44.     KCContactGroup *group1=[KCContactGroup initWithName:@"C" andDetail:@"With names beginning with C" andContacts:[NSMutableArray arrayWithObjects:contact1,contact2, nil]]; 
  45.     [_contacts addObject:group1]; 
  46.      
  47.  
  48.      
  49.     KCContact *contact3=[KCContact initWithFirstName:@"Lee" andLastName:@"Terry" andPhoneNumber:@"18500131238"]; 
  50.     KCContact *contact4=[KCContact initWithFirstName:@"Lee" andLastName:@"Jack" andPhoneNumber:@"18500131239"]; 
  51.     KCContact *contact5=[KCContact initWithFirstName:@"Lee" andLastName:@"Rose" andPhoneNumber:@"18500131240"]; 
  52.     KCContactGroup *group2=[KCContactGroup initWithName:@"L" andDetail:@"With names beginning with L" andContacts:[NSMutableArray arrayWithObjects:contact3,contact4,contact5, nil]]; 
  53.     [_contacts addObject:group2]; 
  54.      
  55.      
  56.      
  57.     KCContact *contact6=[KCContact initWithFirstName:@"Sun" andLastName:@"Kaoru" andPhoneNumber:@"18500131235"]; 
  58.     KCContact *contact7=[KCContact initWithFirstName:@"Sun" andLastName:@"Rosa" andPhoneNumber:@"18500131236"]; 
  59.  
  60.     KCContactGroup *group3=[KCContactGroup initWithName:@"S" andDetail:@"With names beginning with S" andContacts:[NSMutableArray arrayWithObjects:contact6,contact7, nil]]; 
  61.     [_contacts addObject:group3]; 
  62.      
  63.      
  64.     KCContact *contact8=[KCContact initWithFirstName:@"Wang" andLastName:@"Stephone" andPhoneNumber:@"18500131241"]; 
  65.     KCContact *contact9=[KCContact initWithFirstName:@"Wang" andLastName:@"Lucy" andPhoneNumber:@"18500131242"]; 
  66.     KCContact *contact10=[KCContact initWithFirstName:@"Wang" andLastName:@"Lily" andPhoneNumber:@"18500131243"]; 
  67.     KCContact *contact11=[KCContact initWithFirstName:@"Wang" andLastName:@"Emily" andPhoneNumber:@"18500131244"]; 
  68.     KCContact *contact12=[KCContact initWithFirstName:@"Wang" andLastName:@"Andy" andPhoneNumber:@"18500131245"]; 
  69.     KCContactGroup *group4=[KCContactGroup initWithName:@"W" andDetail:@"With names beginning with W" andContacts:[NSMutableArray arrayWithObjects:contact8,contact9,contact10,contact11,contact12, nil]]; 
  70.     [_contacts addObject:group4]; 
  71.      
  72.      
  73.     KCContact *contact13=[KCContact initWithFirs

以上是关于UITableView全面解析的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发系列--UITableView全面解析

iOS开发系列--UITableView全面解析(很详细)

Java注解全面解析

java中构造方法和方法全面解析

iOS 中UITextFiled全面解析

iOS-Block总结 && 全面解析逆向传值