--UIKit之UITableViewhtml
在iOS开发中UITableView能够说是使用最普遍的控件,咱们平时使用的软件中处处均可以看到它的影子,相似于微信、QQ、新浪微博等软件基本上随处都是UITableView。固然它的普遍使用天然离不开它强大的功能,今天这篇文章将针对UITableView重点展开讨论。今天的主要内容包括:api
UITableView有两种风格:UITableViewStylePlain和UITableViewStyleGrouped。这二者操做起来其实并无本质区别,只是后者按分组样式显示前者按照普通样式显示而已。你们先看一下二者的应用:数组
1>分组样式缓存
2>不分组样式性能优化
你们能够看到在UITableView中数据只有行的概念,并无列的概念,由于在手机操做系统中显示多列是不利于操做的。UITableView中每行数据都是一个UITableViewCell,在这个控件中为了显示更多的信息,iOS已经在其内部设置好了多个子控件以供开发者使用。若是咱们查看UITableViewCell的声明文件能够发如今内部有一个UIView控件(contentView,做为其余元素的父控件)、两个UILable控件(textLabel、detailTextLabel)、一个UIImage控件(imageView),分别用于容器、显示内容、详情和图片。使用效果相似于微信、QQ信息列表:微信
固然,这些子控件并不必定要所有使用,具体操做时能够经过UITableViewCellStyle进行设置,具体每一个枚举表示的意思已经在代码中进行了注释:网络
typedef NS_ENUM(NSInteger, UITableViewCellStyle) { UITableViewCellStyleDefault, // 左侧显示textLabel(不显示detailTextLabel),imageView可选(显示在最左边) UITableViewCellStyleValue1, // 左侧显示textLabel、右侧显示detailTextLabel(默认蓝色),imageView可选(显示在最左边) UITableViewCellStyleValue2, // 左侧依次显示textLabel(默认蓝色)和detailTextLabel,imageView可选(显示在最左边) UITableViewCellStyleSubtitle // 左上方显示textLabel,左下方显示detailTextLabel(默认灰色),imageView可选(显示在最左边) };
因为iOS是遵循MVC模式设计的,不少操做都是经过代理和外界沟通的,但对于数据源控件除了代理还有一个数据源属性,经过它和外界进行数据交互。 对于UITableView设置完dataSource后须要实现UITableViewDataSource协议,在这个协议中定义了多种 数据操做方法,下面经过建立一个简单的联系人管理进行演示:mvc
首先咱们须要建立一个联系人模型KCContactapp
KCContact.h函数
// // Contact.h // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> @interface KCContact : NSObject #pragma mark 姓 @property (nonatomic,copy) NSString *firstName; #pragma mark 名 @property (nonatomic,copy) NSString *lastName; #pragma mark 手机号码 @property (nonatomic,copy) NSString *phoneNumber; #pragma mark 带参数的构造函数 -(KCContact *)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName andPhoneNumber:(NSString *)phoneNumber; #pragma mark 取得姓名 -(NSString *)getName; #pragma mark 带参数的静态对象初始化方法 +(KCContact *)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName andPhoneNumber:(NSString *)phoneNumber; @end
KCContact.m
// // Contact.m // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCContact.h" @implementation KCContact -(KCContact *)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName andPhoneNumber:(NSString *)phoneNumber{ if(self=[super init]){ self.firstName=firstName; self.lastName=lastName; self.phoneNumber=phoneNumber; } return self; } -(NSString *)getName{ return [NSString stringWithFormat:@"%@ %@",_lastName,_firstName]; } +(KCContact *)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName andPhoneNumber:(NSString *)phoneNumber{ KCContact *contact1=[[KCContact alloc]initWithFirstName:firstName andLastName:lastName andPhoneNumber:phoneNumber]; return contact1; } @end
为了演示分组显示咱们不妨将一组数据也抽象成模型KCContactGroup
KCContactGroup.h
// // KCContactGroup.h // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> #import "KCContact.h" @interface KCContactGroup : NSObject #pragma mark 组名 @property (nonatomic,copy) NSString *name; #pragma mark 分组描述 @property (nonatomic,copy) NSString *detail; #pragma mark 联系人 @property (nonatomic,strong) NSMutableArray *contacts; #pragma mark 带参数个构造函数 -(KCContactGroup *)initWithName:(NSString *)name andDetail:(NSString *)detail andContacts:(NSMutableArray *)contacts; #pragma mark 静态初始化方法 +(KCContactGroup *)initWithName:(NSString *)name andDetail:(NSString *)detail andContacts:(NSMutableArray *)contacts; @end
KCContactGroup.m
// // KCContactGroup.m // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCContactGroup.h" @implementation KCContactGroup -(KCContactGroup *)initWithName:(NSString *)name andDetail:(NSString *)detail andContacts:(NSMutableArray *)contacts{ if (self=[super init]) { self.name=name; self.detail=detail; self.contacts=contacts; } return self; } +(KCContactGroup *)initWithName:(NSString *)name andDetail:(NSString *)detail andContacts:(NSMutableArray *)contacts{ KCContactGroup *group1=[[KCContactGroup alloc]initWithName:name andDetail:detail andContacts:contacts]; return group1; } @end
而后在viewDidLoad方法中建立一些模拟数据同时实现数据源协议方法:
KCMainViewController.m
// // KCMainViewController.m // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCMainViewController.h" #import "KCContact.h" #import "KCContactGroup.h" @interface KCMainViewController ()<UITableViewDataSource>{ UITableView *_tableView; NSMutableArray *_contacts;//联系人模型 } @end @implementation KCMainViewController - (void)viewDidLoad { [super viewDidLoad]; //初始化数据 [self initData]; //建立一个分组样式的UITableView _tableView=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; //设置数据源,注意必须实现对应的UITableViewDataSource协议 _tableView.dataSource=self; [self.view addSubview:_tableView]; } #pragma mark 加载数据 -(void)initData{ _contacts=[[NSMutableArray alloc]init]; KCContact *contact1=[KCContact initWithFirstName:@"Cui" andLastName:@"Kenshin" andPhoneNumber:@"18500131234"]; KCContact *contact2=[KCContact initWithFirstName:@"Cui" andLastName:@"Tom" andPhoneNumber:@"18500131237"]; KCContactGroup *group1=[KCContactGroup initWithName:@"C" andDetail:@"With names beginning with C" andContacts:[NSMutableArray arrayWithObjects:contact1,contact2, nil]]; [_contacts addObject:group1]; KCContact *contact3=[KCContact initWithFirstName:@"Lee" andLastName:@"Terry" andPhoneNumber:@"18500131238"]; KCContact *contact4=[KCContact initWithFirstName:@"Lee" andLastName:@"Jack" andPhoneNumber:@"18500131239"]; KCContact *contact5=[KCContact initWithFirstName:@"Lee" andLastName:@"Rose" andPhoneNumber:@"18500131240"]; KCContactGroup *group2=[KCContactGroup initWithName:@"L" andDetail:@"With names beginning with L" andContacts:[NSMutableArray arrayWithObjects:contact3,contact4,contact5, nil]]; [_contacts addObject:group2]; KCContact *contact6=[KCContact initWithFirstName:@"Sun" andLastName:@"Kaoru" andPhoneNumber:@"18500131235"]; KCContact *contact7=[KCContact initWithFirstName:@"Sun" andLastName:@"Rosa" andPhoneNumber:@"18500131236"]; KCContactGroup *group3=[KCContactGroup initWithName:@"S" andDetail:@"With names beginning with S" andContacts:[NSMutableArray arrayWithObjects:contact6,contact7, nil]]; [_contacts addObject:group3]; KCContact *contact8=[KCContact initWithFirstName:@"Wang" andLastName:@"Stephone" andPhoneNumber:@"18500131241"]; KCContact *contact9=[KCContact initWithFirstName:@"Wang" andLastName:@"Lucy" andPhoneNumber:@"18500131242"]; KCContact *contact10=[KCContact initWithFirstName:@"Wang" andLastName:@"Lily" andPhoneNumber:@"18500131243"]; KCContact *contact11=[KCContact initWithFirstName:@"Wang" andLastName:@"Emily" andPhoneNumber:@"18500131244"]; KCContact *contact12=[KCContact initWithFirstName:@"Wang" andLastName:@"Andy" andPhoneNumber:@"18500131245"]; KCContactGroup *group4=[KCContactGroup initWithName:@"W" andDetail:@"With names beginning with W" andContacts:[NSMutableArray arrayWithObjects:contact8,contact9,contact10,contact11,contact12, nil]]; [_contacts addObject:group4]; KCContact *contact13=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joy" andPhoneNumber:@"18500131246"]; KCContact *contact14=[KCContact initWithFirstName:@"Zhang" andLastName:@"Vivan" andPhoneNumber:@"18500131247"]; KCContact *contact15=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joyse" andPhoneNumber:@"18500131248"]; KCContactGroup *group5=[KCContactGroup initWithName:@"Z" andDetail:@"With names beginning with Z" andContacts:[NSMutableArray arrayWithObjects:contact13,contact14,contact15, nil]]; [_contacts addObject:group5]; } #pragma mark - 数据源方法 #pragma mark 返回分组数 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ NSLog(@"计算分组数"); return _contacts.count; } #pragma mark 返回每组行数 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ NSLog(@"计算每组(组%i)行数",section); KCContactGroup *group1=_contacts[section]; return group1.contacts.count; } #pragma mark返回每行的单元格 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ //NSIndexPath是一个结构体,记录了组和行信息 NSLog(@"生成单元格(组:%i,行%i)",indexPath.section,indexPath.row); KCContactGroup *group=_contacts[indexPath.section]; KCContact *contact=group.contacts[indexPath.row]; UITableViewCell *cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil]; cell.textLabel.text=[contact getName]; cell.detailTextLabel.text=contact.phoneNumber; return cell; } #pragma mark 返回每组头标题名称 -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ NSLog(@"生成组(组%i)名称",section); KCContactGroup *group=_contacts[section]; return group.name; } #pragma mark 返回每组尾部说明 -(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section{ NSLog(@"生成尾部(组%i)详情",section); KCContactGroup *group=_contacts[section]; return group.detail; } @end
运行能够看到以下效果:
你们在使用iPhone通信录时会发现右侧能够按字母检索,使用起来很方便,其实这个功能使用UITableView实现很简单,只要实现数据源协议的一个方法,构建一个分组标题的数组便可实现。数组元素的内容和组标题内容未必彻底一致,UITableView是按照数组元素的索引和每组数据索引顺序来定位的而不是按内容查找。
#pragma mark 返回每组标题索引 -(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{ NSLog(@"生成组索引"); NSMutableArray *indexs=[[NSMutableArray alloc]init]; for(KCContactGroup *group in _contacts){ [indexs addObject:group.name]; } return indexs; }
效果以下:
须要注意的是上面几个重点方法的执行顺序,请看下图:
值得指出的是生成单元格的方法并非一次所有调用,而是只会生产当前显示在界面上的单元格,当用户滚动操做时再显示其余单元格。
注意:随着咱们的应用愈来愈复杂,可能常常须要调试程序,在iOS中默认状况下不能定位到错误代码行,咱们能够经过以下设置让程序定位到出错代码行:Show the Breakpoint navigator—Add Exception breakpoint。
上面咱们已经看到通信录的简单实现,可是咱们发现单元格高度、分组标题高度以及尾部说明的高度都须要调整,此时就须要使用代理方法。UITableView代理方法有不少,例如监听单元格显示周期、监听单元格选择编辑操做、设置是否高亮显示单元格、设置行高等。
#pragma mark - 代理方法 #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; }
在iOS中点击某联系我的就能够呼叫这个联系人,这时就须要监听点击操做,这里就不演示呼叫联系人操做了,咱们演示一下修改人员信息的操做。
KCMainViewContrller.m
// // KCMainViewController.m // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCMainViewController.h" #import "KCContact.h" #import "KCContactGroup.h" @interface KCMainViewController ()<UITableViewDataSource,UITableViewDelegate,UIAlertViewDelegate>{ UITableView *_tableView; NSMutableArray *_contacts;//联系人模型 NSIndexPath *_selectedIndexPath;//当前选中的组和行 } @end @implementation KCMainViewController - (void)viewDidLoad { [super viewDidLoad]; //初始化数据 [self initData]; //建立一个分组样式的UITableView _tableView=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; //设置数据源,注意必须实现对应的UITableViewDataSource协议 _tableView.dataSource=self; //设置代理 _tableView.delegate=self; [self.view addSubview:_tableView]; } #pragma mark 加载数据 -(void)initData{ _contacts=[[NSMutableArray alloc]init]; KCContact *contact1=[KCContact initWithFirstName:@"Cui" andLastName:@"Kenshin" andPhoneNumber:@"18500131234"]; KCContact *contact2=[KCContact initWithFirstName:@"Cui" andLastName:@"Tom" andPhoneNumber:@"18500131237"]; KCContactGroup *group1=[KCContactGroup initWithName:@"C" andDetail:@"With names beginning with C" andContacts:[NSMutableArray arrayWithObjects:contact1,contact2, nil]]; [_contacts addObject:group1]; KCContact *contact3=[KCContact initWithFirstName:@"Lee" andLastName:@"Terry" andPhoneNumber:@"18500131238"]; KCContact *contact4=[KCContact initWithFirstName:@"Lee" andLastName:@"Jack" andPhoneNumber:@"18500131239"]; KCContact *contact5=[KCContact initWithFirstName:@"Lee" andLastName:@"Rose" andPhoneNumber:@"18500131240"]; KCContactGroup *group2=[KCContactGroup initWithName:@"L" andDetail:@"With names beginning with L" andContacts:[NSMutableArray arrayWithObjects:contact3,contact4,contact5, nil]]; [_contacts addObject:group2]; KCContact *contact6=[KCContact initWithFirstName:@"Sun" andLastName:@"Kaoru" andPhoneNumber:@"18500131235"]; KCContact *contact7=[KCContact initWithFirstName:@"Sun" andLastName:@"Rosa" andPhoneNumber:@"18500131236"]; KCContactGroup *group3=[KCContactGroup initWithName:@"S" andDetail:@"With names beginning with S" andContacts:[NSMutableArray arrayWithObjects:contact6,contact7, nil]]; [_contacts addObject:group3]; KCContact *contact8=[KCContact initWithFirstName:@"Wang" andLastName:@"Stephone" andPhoneNumber:@"18500131241"]; KCContact *contact9=[KCContact initWithFirstName:@"Wang" andLastName:@"Lucy" andPhoneNumber:@"18500131242"]; KCContact *contact10=[KCContact initWithFirstName:@"Wang" andLastName:@"Lily" andPhoneNumber:@"18500131243"]; KCContact *contact11=[KCContact initWithFirstName:@"Wang" andLastName:@"Emily" andPhoneNumber:@"18500131244"]; KCContact *contact12=[KCContact initWithFirstName:@"Wang" andLastName:@"Andy" andPhoneNumber:@"18500131245"]; KCContactGroup *group4=[KCContactGroup initWithName:@"W" andDetail:@"With names beginning with W" andContacts:[NSMutableArray arrayWithObjects:contact8,contact9,contact10,contact11,contact12, nil]]; [_contacts addObject:group4]; KCContact *contact13=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joy" andPhoneNumber:@"18500131246"]; KCContact *contact14=[KCContact initWithFirstName:@"Zhang" andLastName:@"Vivan" andPhoneNumber:@"18500131247"]; KCContact *contact15=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joyse" andPhoneNumber:@"18500131248"]; KCContactGroup *group5=[KCContactGroup initWithName:@"Z" andDetail:@"With names beginning with Z" andContacts:[NSMutableArray arrayWithObjects:contact13,contact14,contact15, nil]]; [_contacts addObject:group5]; } #pragma mark - 数据源方法 #pragma mark 返回分组数 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ NSLog(@"计算分组数"); return _contacts.count; } #pragma mark 返回每组行数 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ NSLog(@"计算每组(组%i)行数",section); KCContactGroup *group1=_contacts[section]; return group1.contacts.count; } #pragma mark返回每行的单元格 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ //NSIndexPath是一个对象,记录了组和行信息 NSLog(@"生成单元格(组:%i,行%i)",indexPath.section,indexPath.row); KCContactGroup *group=_contacts[indexPath.section]; KCContact *contact=group.contacts[indexPath.row]; UITableViewCell *cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil]; cell.textLabel.text=[contact getName]; cell.detailTextLabel.text=contact.phoneNumber; return cell; } #pragma mark 返回每组头标题名称 -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ NSLog(@"生成组(组%i)名称",section); KCContactGroup *group=_contacts[section]; return group.name; } #pragma mark 返回每组尾部说明 -(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section{ NSLog(@"生成尾部(组%i)详情",section); KCContactGroup *group=_contacts[section]; return group.detail; } #pragma mark 返回每组标题索引 -(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{ NSLog(@"生成组索引"); NSMutableArray *indexs=[[NSMutableArray alloc]init]; for(KCContactGroup *group in _contacts){ [indexs addObject:group.name]; } return indexs; } #pragma mark - 代理方法 #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; } #pragma mark 点击行 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ _selectedIndexPath=indexPath; KCContactGroup *group=_contacts[indexPath.section]; KCContact *contact=group.contacts[indexPath.row]; //建立弹出窗口 UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"System Info" message:[contact getName] delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil]; alert.alertViewStyle=UIAlertViewStylePlainTextInput; //设置窗口内容样式 UITextField *textField= [alert textFieldAtIndex:0]; //取得文本框 textField.text=contact.phoneNumber; //设置文本框内容 [alert show]; //显示窗口 } #pragma mark 窗口的代理方法,用户保存数据 -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ //当点击了第二个按钮(OK) if (buttonIndex==1) { UITextField *textField= [alertView textFieldAtIndex:0]; //修改模型数据 KCContactGroup *group=_contacts[_selectedIndexPath.section]; KCContact *contact=group.contacts[_selectedIndexPath.row]; contact.phoneNumber=textField.text; //刷新表格 [_tableView reloadData]; } } #pragma mark 重写状态样式方法 -(UIStatusBarStyle)preferredStatusBarStyle{ return UIStatusBarStyleLightContent; } @end
在上面的代码中咱们经过修改模型来改变UI显示,这种方式是经典的MVC应用,在后面的代码中会常常看到。固然UI的刷新使用了UITableView的reloadData方法,该方法会从新调用数据源方法,包括计算分组、计算每一个分组的行数,生成单元格等刷新整个UITableView。固然这种方式在实际开发中是不可取的,咱们不可能由于修改了一我的的信息就刷新整个UITableViewView,此时咱们须要采用局部刷新。局部刷新使用起来很简单,只须要调用UITableView的另一个方法:
#pragma mark 窗口的代理方法,用户保存数据 -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ //当点击了第二个按钮(OK) if (buttonIndex==1) { UITextField *textField= [alertView textFieldAtIndex:0]; //修改模型数据 KCContactGroup *group=_contacts[_selectedIndexPath.section]; KCContact *contact=group.contacts[_selectedIndexPath.row]; contact.phoneNumber=textField.text; //刷新表格 NSArray *indexPaths=@[_selectedIndexPath];//须要局部刷新的单元格的组、行 [_tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationLeft];//后面的参数表明更新时的动画 } }
前面已经说过UITableView中的单元格cell是在显示到用户可视区域后建立的,那么若是用户往下滚动就会继续建立显示在屏幕上的单元格,若是用户向上滚动返回到查看过的内容时一样会从新建立以前已经建立过的单元格。如此一来即便UITableView的内容不是太多,若是用户反复的上下滚动,内存也会瞬间飙升,更况且不少时候UITableView的内容是不少的(例如微博展现列表,基本向下滚动是没有底限的)。
前面一节中咱们曾经提到过如何优化UIScrollView,当时就是利用有限的UIImageView动态切换其内容来尽量减小资源占用。一样的,在UITableView中也能够采用相似的方式,只是这时咱们不是在滚动到指定位置后更改滚动的位置而是要将当前没有显示的Cell从新显示在将要显示的Cell的位置而后更新其内容。缘由就是UITableView中的Cell结构布局多是不一样的,经过从新定位是不可取的,而是须要重用已经再也不界面显示的已建立过的Cell。
固然,听起来这么作比较复杂,其实实现起来很简单,由于UITableView已经为咱们实现了这种机制。在UITableView内部有一个缓存池,初始化时使用initWithStyle:(UITableViewCellStyle) reuseIdentifier:(NSString *)方法指定一个可重用标识,就能够将这个cell放到缓存池。而后在使用时使用指定的标识去缓存池中取得对应的cell而后修改cell内容便可。
#pragma mark返回每行的单元格 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ //NSIndexPath是一个对象,记录了组和行信息 NSLog(@"生成单元格(组:%i,行%i)",indexPath.section,indexPath.row); KCContactGroup *group=_contacts[indexPath.section]; KCContact *contact=group.contacts[indexPath.row]; //因为此方法调用十分频繁,cell的标示声明成静态变量有利于性能优化 static NSString *cellIdentifier=@"UITableViewCellIdentifierKey1"; //首先根据标识去缓存池取 UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier]; //若是缓存池没有到则从新建立并放到缓存池中 if(!cell){ cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier]; } cell.textLabel.text=[contact getName]; cell.detailTextLabel.text=contact.phoneNumber; NSLog(@"cell:%@",cell); return cell; }
上面的代码中已经打印了cell的地址,若是你们运行测试上下滚动UITableView会发现滚动时建立的cell地址是初始化时已经建立的。
这里再次给你们强调两点:
UITableViewCell是构建一个UITableView的基础,在UITableViewCell内部有一个UIView控件做为其余内容的容器,它上面有一个UIImageView和两个UILabel,经过UITableViewCellStyle属性能够对其样式进行控制。其结构以下:
有时候咱们会发现不少UITableViewCell右侧能够显示不一样的图标,在iOS中称之为访问器,点击能够触发不一样的事件,例如设置功能:
要设置这些图标只须要设置UITableViewCell的accesoryType属性,这是一个枚举类型具体含义以下:
typedef NS_ENUM(NSInteger, UITableViewCellAccessoryType) { UITableViewCellAccessoryNone, // 不显示任何图标 UITableViewCellAccessoryDisclosureIndicator, // 跳转指示图标 UITableViewCellAccessoryDetailDisclosureButton, // 内容详情图标和跳转指示图标 UITableViewCellAccessoryCheckmark, // 勾选图标 UITableViewCellAccessoryDetailButton NS_ENUM_AVAILABLE_IOS(7_0) // 内容详情图标 };
例如在最近通话中咱们一般设置为详情图标,点击能够查看联系人详情:
很明显iOS设置中第一个accessoryType不在枚举之列,右侧的访问器类型是UISwitch控件,那么如何显示自定义的访问器呢?其实只要设置UITableViewCell的accessoryView便可,它支持任何UIView控件。假设咱们在通信录每组第一行放一个UISwitch,同时切换时能够输出对应信息:
// // KCMainViewController.m // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCMainViewController.h" #import "KCContact.h" #import "KCContactGroup.h" @interface KCMainViewController ()<UITableViewDataSource,UITableViewDelegate,UIAlertViewDelegate>{ UITableView *_tableView; NSMutableArray *_contacts;//联系人模型 NSIndexPath *_selectedIndexPath;//当前选中的组和行 } @end @implementation KCMainViewController - (void)viewDidLoad { [super viewDidLoad]; //初始化数据 [self initData]; //建立一个分组样式的UITableView _tableView=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; //设置数据源,注意必须实现对应的UITableViewDataSource协议 _tableView.dataSource=self; //设置代理 _tableView.delegate=self; [self.view addSubview:_tableView]; } #pragma mark 加载数据 -(void)initData{ _contacts=[[NSMutableArray alloc]init]; KCContact *contact1=[KCContact initWithFirstName:@"Cui" andLastName:@"Kenshin" andPhoneNumber:@"18500131234"]; KCContact *contact2=[KCContact initWithFirstName:@"Cui" andLastName:@"Tom" andPhoneNumber:@"18500131237"]; KCContactGroup *group1=[KCContactGroup initWithName:@"C" andDetail:@"With names beginning with C" andContacts:[NSMutableArray arrayWithObjects:contact1,contact2, nil]]; [_contacts addObject:group1]; KCContact *contact3=[KCContact initWithFirstName:@"Lee" andLastName:@"Terry" andPhoneNumber:@"18500131238"]; KCContact *contact4=[KCContact initWithFirstName:@"Lee" andLastName:@"Jack" andPhoneNumber:@"18500131239"]; KCContact *contact5=[KCContact initWithFirstName:@"Lee" andLastName:@"Rose" andPhoneNumber:@"18500131240"]; KCContactGroup *group2=[KCContactGroup initWithName:@"L" andDetail:@"With names beginning with L" andContacts:[NSMutableArray arrayWithObjects:contact3,contact4,contact5, nil]]; [_contacts addObject:group2]; KCContact *contact6=[KCContact initWithFirstName:@"Sun" andLastName:@"Kaoru" andPhoneNumber:@"18500131235"]; KCContact *contact7=[KCContact initWithFirstName:@"Sun" andLastName:@"Rosa" andPhoneNumber:@"18500131236"]; KCContactGroup *group3=[KCContactGroup initWithName:@"S" andDetail:@"With names beginning with S" andContacts:[NSMutableArray arrayWithObjects:contact6,contact7, nil]]; [_contacts addObject:group3]; KCContact *contact8=[KCContact initWithFirstName:@"Wang" andLastName:@"Stephone" andPhoneNumber:@"18500131241"]; KCContact *contact9=[KCContact initWithFirstName:@"Wang" andLastName:@"Lucy" andPhoneNumber:@"18500131242"]; KCContact *contact10=[KCContact initWithFirstName:@"Wang" andLastName:@"Lily" andPhoneNumber:@"18500131243"]; KCContact *contact11=[KCContact initWithFirstName:@"Wang" andLastName:@"Emily" andPhoneNumber:@"18500131244"]; KCContact *contact12=[KCContact initWithFirstName:@"Wang" andLastName:@"Andy" andPhoneNumber:@"18500131245"]; KCContactGroup *group4=[KCContactGroup initWithName:@"W" andDetail:@"With names beginning with W" andContacts:[NSMutableArray arrayWithObjects:contact8,contact9,contact10,contact11,contact12, nil]]; [_contacts addObject:group4]; KCContact *contact13=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joy" andPhoneNumber:@"18500131246"]; KCContact *contact14=[KCContact initWithFirstName:@"Zhang" andLastName:@"Vivan" andPhoneNumber:@"18500131247"]; KCContact *contact15=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joyse" andPhoneNumber:@"18500131248"]; KCContactGroup *group5=[KCContactGroup initWithName:@"Z" andDetail:@"With names beginning with Z" andContacts:[NSMutableArray arrayWithObjects:contact13,contact14,contact15, nil]]; [_contacts addObject:group5]; } #pragma mark - 数据源方法 #pragma mark 返回分组数 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ NSLog(@"计算分组数"); return _contacts.count; } #pragma mark 返回每组行数 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ NSLog(@"计算每组(组%i)行数",section); KCContactGroup *group1=_contacts[section]; return group1.contacts.count; } #pragma mark返回每行的单元格 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ //NSIndexPath是一个对象,记录了组和行信息 NSLog(@"生成单元格(组:%i,行%i)",indexPath.section,indexPath.row); KCContactGroup *group=_contacts[indexPath.section]; KCContact *contact=group.contacts[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=[contact getName]; cell.detailTextLabel.text=contact.phoneNumber; NSLog(@"cell:%@",cell); return cell; } #pragma mark 返回每组头标题名称 -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ NSLog(@"生成组(组%i)名称",section); KCContactGroup *group=_contacts[section]; return group.name; } #pragma mark 返回每组尾部说明 -(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section{ NSLog(@"生成尾部(组%i)详情",section); KCContactGroup *group=_contacts[section]; return group.detail; } #pragma mark 返回每组标题索引 -(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{ NSLog(@"生成组索引"); NSMutableArray *indexs=[[NSMutableArray alloc]init]; for(KCContactGroup *group in _contacts){ [indexs addObject:group.name]; } return indexs; } #pragma mark - 代理方法 #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; } #pragma mark 点击行 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ _selectedIndexPath=indexPath; KCContactGroup *group=_contacts[indexPath.section]; KCContact *contact=group.contacts[indexPath.row]; //建立弹出窗口 UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"System Info" message:[contact getName] delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil]; alert.alertViewStyle=UIAlertViewStylePlainTextInput; //设置窗口内容样式 UITextField *textField= [alert textFieldAtIndex:0]; //取得文本框 textField.text=contact.phoneNumber; //设置文本框内容 [alert show]; //显示窗口 } #pragma mark 窗口的代理方法,用户保存数据 -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ //当点击了第二个按钮(OK) if (buttonIndex==1) { UITextField *textField= [alertView textFieldAtIndex:0]; //修改模型数据 KCContactGroup *group=_contacts[_selectedIndexPath.section]; KCContact *contact=group.contacts[_selectedIndexPath.row]; contact.phoneNumber=textField.text; //刷新表格 NSArray *indexPaths=@[_selectedIndexPath];//须要局部刷新的单元格的组、行 [_tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationLeft];//后面的参数代码更新时的动画 } } #pragma mark 重写状态样式方法 -(UIStatusBarStyle)preferredStatusBarStyle{ return UIStatusBarStyleLightContent; } #pragma mark 切换开关转化事件 -(void)switchValueChange:(UISwitch *)sw{ NSLog(@"section:%i,switch:%i",sw.tag, sw.on); } @end
最终运行效果:
注意:
虽然系统自带的UITableViewCell已经够强大了,可是不少时候这并不能知足咱们的需求。例如新浪微博的Cell就没有那么简单:
没错,这个界面布局也是UITableView实现的,其中的内容就是UITableViewCell,只是这个UITableViewCell是用户自定义实现的。固然要实现上面的UITableViewCell三言两语咱们是说不完的,这里咱们实现一个简化版本,界面原型以下:
咱们对具体控件进行拆分:
在这个界面中有2个UIImageView控件和4个UILabel,整个界面显示效果相似于新浪微博的消息内容界面,可是又在新浪微博基础上进行了精简以致于利用现有知识可以顺利开发出来。
在前面的内容中咱们的数据都是手动构建的,在实际开发中天然不会这么作,这里咱们不妨将微博数据存储到plist文件中而后从plist文件读取数据构建模型对象(实际开发微博固然须要进行网络数据请求,这里只是进行模拟就再也不演示网络请求的内容)。假设plist文件内容以下:
接下来就定义一个KCStatusTableViewCell实现UITableViewCell,通常实现自定义UITableViewCell须要分为两步:第一初始化控件;第二设置数据,从新设置控件frame。缘由就是自定义Cell通常没法固定高度,不少时候高度须要随着内容改变。此外因为在单元格内部是没法控制单元格高度的,所以通常会定义一个高度属性用于在UITableView的代理事件中设置每一个单元格高度。
1.首先看一下微博模型KCStatus,这个模型主要的方法就是根据plist字典内容生成微博对象:
KCStatus.h
// // KCStatus.h // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> @interface KCStatus : NSObject #pragma mark - 属性 @property (nonatomic,assign) long long Id;//微博id @property (nonatomic,copy) NSString *profileImageUrl;//头像 @property (nonatomic,copy) NSString *userName;//发送用户 @property (nonatomic,copy) NSString *mbtype;//会员类型 @property (nonatomic,copy) NSString *createdAt;//建立时间 @property (nonatomic,copy) NSString *source;//设备来源 @property (nonatomic,copy) NSString *text;//微博内容 #pragma mark - 方法 #pragma mark 根据字典初始化微博对象 -(KCStatus *)initWithDictionary:(NSDictionary *)dic; #pragma mark 初始化微博对象(静态方法) +(KCStatus *)statusWithDictionary:(NSDictionary *)dic; @end
KCStatus.m
// // KCStatus.m // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCStatus.h" @implementation KCStatus #pragma mark 根据字典初始化微博对象 -(KCStatus *)initWithDictionary:(NSDictionary *)dic{ if(self=[super init]){ self.Id=[dic[@"Id"] longLongValue]; self.profileImageUrl=dic[@"profileImageUrl"]; self.userName=dic[@"userName"]; self.mbtype=dic[@"mbtype"]; self.createdAt=dic[@"createdAt"]; self.source=dic[@"source"]; self.text=dic[@"text"]; } return self; } #pragma mark 初始化微博对象(静态方法) +(KCStatus *)statusWithDictionary:(NSDictionary *)dic{ KCStatus *status=[[KCStatus alloc]initWithDictionary:dic]; return status; } -(NSString *)source{ return [NSString stringWithFormat:@"来自 %@",_source]; } @end
2.而后看一下自定义的Cell
KCStatusTableViewCell.h
// // KCStatusTableViewCell.h // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <UIKit/UIKit.h> @class KCStatus; @interface KCStatusTableViewCell : UITableViewCell #pragma mark 微博对象 @property (nonatomic,strong) KCStatus *status; #pragma mark 单元格高度 @property (assign,nonatomic) CGFloat height; @end
KCStatusTableViewCell.m
// // KCStatusTableViewCell.m // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCStatusTableViewCell.h" #import "KCStatus.h" #define KCColor(r,g,b) [UIColor colorWithHue:r/255.0 saturation:g/255.0 brightness:b/255.0 alpha:1] //颜色宏定义 #define kStatusTableViewCellControlSpacing 10 //控件间距 #define kStatusTableViewCellBackgroundColor KCColor(251,251,251) #define kStatusGrayColor KCColor(50,50,50) #define kStatusLightGrayColor KCColor(120,120,120) #define kStatusTableViewCellAvatarWidth 40 //头像宽度 #define kStatusTableViewCellAvatarHeight kStatusTableViewCellAvatarWidth #define kStatusTableViewCellUserNameFontSize 14 #define kStatusTableViewCellMbTypeWidth 13 //会员图标宽度 #define kStatusTableViewCellMbTypeHeight kStatusTableViewCellMbTypeWidth #define kStatusTableViewCellCreateAtFontSize 12 #define kStatusTableViewCellSourceFontSize 12 #define kStatusTableViewCellTextFontSize 14 @interface KCStatusTableViewCell(){ UIImageView *_avatar;//头像 UIImageView *_mbType;//会员类型 UILabel *_userName; UILabel *_createAt; UILabel *_source; UILabel *_text; } @end @implementation KCStatusTableViewCell - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { [self initSubView]; } return self; } #pragma mark 初始化视图 -(void)initSubView{ //头像控件 _avatar=[[UIImageView alloc]init]; [self addSubview:_avatar]; //用户名 _userName=[[UILabel alloc]init]; _userName.textColor=kStatusGrayColor; _userName.font=[UIFont systemFontOfSize:kStatusTableViewCellUserNameFontSize]; [self addSubview:_userName]; //会员类型 _mbType=[[UIImageView alloc]init]; [self addSubview:_mbType]; //日期 _createAt=[[UILabel alloc]init]; _createAt.textColor=kStatusLightGrayColor; _createAt.font=[UIFont systemFontOfSize:kStatusTableViewCellCreateAtFontSize]; [self addSubview:_createAt]; //设备 _source=[[UILabel alloc]init]; _source.textColor=kStatusLightGrayColor; _source.font=[UIFont systemFontOfSize:kStatusTableViewCellSourceFontSize]; [self addSubview:_source]; //内容 _text=[[UILabel alloc]init]; _text.textColor=kStatusGrayColor; _text.font=[UIFont systemFontOfSize:kStatusTableViewCellTextFontSize]; _text.numberOfLines=0; // _text.lineBreakMode=NSLineBreakByWordWrapping; [self addSubview:_text]; } #pragma mark 设置微博 -(void)setStatus:(KCStatus *)status{ //设置头像大小和位置 CGFloat avatarX=10,avatarY=10; CGRect avatarRect=CGRectMake(avatarX, avatarY, kStatusTableViewCellAvatarWidth, kStatusTableViewCellAvatarHeight); _avatar.image=[UIImage imageNamed:status.profileImageUrl]; _avatar.frame=avatarRect; //设置会员图标大小和位置 CGFloat userNameX= CGRectGetMaxX(_avatar.frame)+kStatusTableViewCellControlSpacing ; CGFloat userNameY=avatarY; //根据文本内容取得文本占用空间大小 CGSize userNameSize=[status.userName sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:kStatusTableViewCellUserNameFontSize]}]; CGRect userNameRect=CGRectMake(userNameX, userNameY, userNameSize.width,userNameSize.height); _userName.text=status.userName; _userName.frame=userNameRect; //设置会员图标大小和位置 CGFloat mbTypeX=CGRectGetMaxX(_userName.frame)+kStatusTableViewCellControlSpacing; CGFloat mbTypeY=avatarY; CGRect mbTypeRect=CGRectMake(mbTypeX, mbTypeY, kStatusTableViewCellMbTypeWidth, kStatusTableViewCellMbTypeHeight); _mbType.image=[UIImage imageNamed:status.mbtype]; _mbType.frame=mbTypeRect; //设置发布日期大小和位置 CGSize createAtSize=[status.createdAt sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:kStatusTableViewCellCreateAtFontSize]}]; CGFloat createAtX=userNameX; CGFloat createAtY=CGRectGetMaxY(_avatar.frame)-createAtSize.height; CGRect createAtRect=CGRectMake(createAtX, createAtY, createAtSize.width, createAtSize.height); _createAt.text=status.createdAt; _createAt.frame=createAtRect; //设置设备信息大小和位置 CGSize sourceSize=[status.source sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:kStatusTableViewCellSourceFontSize]}]; CGFloat sourceX=CGRectGetMaxX(_createAt.frame)+kStatusTableViewCellControlSpacing; CGFloat sourceY=createAtY; CGRect sourceRect=CGRectMake(sourceX, sourceY, sourceSize.width,sourceSize.height); _source.text=status.source; _source.frame=sourceRect; //设置微博内容大小和位置 CGFloat textX=avatarX; CGFloat textY=CGRectGetMaxY(_avatar.frame)+kStatusTableViewCellControlSpacing; CGFloat textWidth=self.frame.size.width-kStatusTableViewCellControlSpacing*2; CGSize textSize=[status.text boundingRectWithSize:CGSizeMake(textWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:kStatusTableViewCellTextFontSize]} context:nil].size; CGRect textRect=CGRectMake(textX, textY, textSize.width, textSize.height); _text.text=status.text; _text.frame=textRect; _height=CGRectGetMaxY(_text.frame)+kStatusTableViewCellControlSpacing; } #pragma mark 重写选择事件,取消选中 -(void)setSelected:(BOOL)selected animated:(BOOL)animated{ } @end
这是咱们自定义Cell这个例子的核心,自定义Cell分为两个步骤:首先要进行各类控件的初始化工做,这个过程当中只要将控件放到Cell的View中同时设置控件显示内容的格式(字体大小、颜色等)便可;而后在数据对象设置方法中进行各个控件的布局(大小、位置)。在代码中有几点须要重点提示你们:
3.最后咱们看一下自定义Cell的使用过程:
KCStatusViewController.m
// // KCCutomCellViewController.m // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCStatusCellViewController.h" #import "KCStatus.h" #import "KCStatusTableViewCell.h" @interface KCStatusCellViewController ()<UITableViewDataSource,UITableViewDelegate,UIAlertViewDelegate>{ UITableView *_tableView; NSMutableArray *_status; NSMutableArray *_statusCells;//存储cell,用于计算高度 } @end @implementation KCStatusCellViewController - (void)viewDidLoad { [super viewDidLoad]; //初始化数据 [self initData]; //建立一个分组样式的UITableView _tableView=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; //设置数据源,注意必须实现对应的UITableViewDataSource协议 _tableView.dataSource=self; //设置代理 _tableView.delegate=self; [self.view addSubview:_tableView]; } #pragma mark 加载数据 -(void)initData{ NSString *path=[[NSBundle mainBundle] pathForResource:@"StatusInfo" ofType:@"plist"]; NSArray *array=[NSArray arrayWithContentsOfFile:path]; _status=[[NSMutableArray alloc]init]; _statusCells=[[NSMutableArray alloc]init]; [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [_status addObject:[KCStatus statusWithDictionary:obj]]; KCStatusTableViewCell *cell=[[KCStatusTableViewCell alloc]init]; [_statusCells addObject:cell]; }]; } #pragma mark - 数据源方法 #pragma mark 返回分组数 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ return 1; } #pragma mark 返回每组行数 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return _status.count; } #pragma mark返回每行的单元格 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *cellIdentifier=@"UITableViewCellIdentifierKey1"; KCStatusTableViewCell *cell; cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if(!cell){ cell=[[KCStatusTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; } //在此设置微博,以便从新布局 KCStatus *status=_status[indexPath.row]; cell.status=status; return cell; } #pragma mark - 代理方法 #pragma mark 从新设置单元格高度 -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ //KCStatusTableViewCell *cell=[tableView cellForRowAtIndexPath:indexPath]; KCStatusTableViewCell *cell= _statusCells[indexPath.row]; cell.status=_status[indexPath.row]; return cell.height; } #pragma mark 重写状态样式方法 -(UIStatusBarStyle)preferredStatusBarStyle{ return UIStatusBarStyleLightContent; } @end
这个类中须要重点强调一下:Cell的高度须要从新设置(前面说过不管Cell内部设置多高都没有用,须要从新设置),这里采用的方法是首先建立对应的Cell,而后在- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;方法中设置微博数据计算高度通知UITableView。
最后咱们看一下运行的效果:
UITableView和UITableViewCell提供了强大的操做功能,这一节中会重点讨论删除、增长、排序等操做。为了方便演示咱们仍是在以前的通信录的基础上演示,在此以前先来给视图控制器添加一个工具条,在工具条左侧放一个删除按钮,右侧放一个添加按钮:
#pragma mark 添加工具栏 -(void)addToolbar{ CGRect frame=self.view.frame; _toolbar=[[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, frame.size.width, kContactToolbarHeight)]; // _toolbar.backgroundColor=[UIColor colorWithHue:246/255.0 saturation:246/255.0 brightness:246/255.0 alpha:1]; [self.view addSubview:_toolbar]; UIBarButtonItem *removeButton=[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(remove)]; UIBarButtonItem *flexibleButton=[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; UIBarButtonItem *addButton=[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(add)]; NSArray *buttonArray=[NSArray arrayWithObjects:removeButton,flexibleButton,addButton, nil]; _toolbar.items=buttonArray; }
在UITableView中不管是删除操做仍是添加操做都是经过修改UITableView的编辑状态来改变的(除非你不用UITableView自带的删除功能)。在删除按钮中咱们设置UITableView的编辑状态:
#pragma mark 删除 -(void)remove{ //直接经过下面的方法设置编辑状态没有动画 //_tableView.editing=!_tableView.isEditing; [_tableView setEditing:!_tableView.isEditing animated:true]; }
点击删除按钮会在Cell的左侧显示删除按钮:
此时点击左侧删除图标右侧出现删除:
用过iOS的朋友都知道,通常这种Cell若是向左滑动右侧就会出现删除按钮直接删除就能够了。其实实现这个功能只要实现代理-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath;方法,只要实现了此方法向左滑动就会显示删除按钮。只要点击删除按钮这个方法就会调用,可是须要注意的是不管是删除仍是添加都是执行这个方法,只是第二个参数类型不一样。下面看一下具体的删除实现:
#pragma mark 删除操做 //实现了此方法向左滑动就会显示删除按钮 -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{ KCContactGroup *group =_contacts[indexPath.section]; KCContact *contact=group.contacts[indexPath.row]; if (editingStyle==UITableViewCellEditingStyleDelete) { [group.contacts removeObject:contact]; //考虑到性能这里不建议使用reloadData //[tableView reloadData]; //使用下面的方法既能够局部刷新又有动画效果 [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom]; //若是当前组中没有数据则移除组刷新整个表格 if (group.contacts.count==0) { [_contacts removeObject:group]; [tableView reloadData]; } } }
从这段代码咱们再次看到了MVC的思想,要修改UI先修改数据。并且咱们看到了另外一个刷新表格的方法- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;,使用这个方法能够再删除以后刷新对应的单元格。效果以下:
添加和删除操做都是设置UITableView的编辑状态,具体是添加仍是删除须要根据代理方法-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;的返回值来肯定。所以这里咱们定义一个变量来记录点击了哪一个按钮,根据点击按钮的不一样在这个方法中返回不一样的值。
#pragma mark 取得当前操做状态,根据不一样的状态左侧出现不一样的操做按钮 -(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{ if (_isInsert) { return UITableViewCellEditingStyleInsert; } return UITableViewCellEditingStyleDelete; }
#pragma mark 编辑操做(删除或添加) //实现了此方法向左滑动就会显示删除(或添加)图标 -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{ KCContactGroup *group =_contacts[indexPath.section]; KCContact *contact=group.contacts[indexPath.row]; if (editingStyle==UITableViewCellEditingStyleDelete) { [group.contacts removeObject:contact]; //考虑到性能这里不建议使用reloadData //[tableView reloadData]; //使用下面的方法既能够局部刷新又有动画效果 [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom]; //若是当前组中没有数据则移除组刷新整个表格 if (group.contacts.count==0) { [_contacts removeObject:group]; [tableView reloadData]; } }else if(editingStyle==UITableViewCellEditingStyleInsert){ KCContact *newContact=[[KCContact alloc]init]; newContact.firstName=@"first"; newContact.lastName=@"last"; newContact.phoneNumber=@"12345678901"; [group.contacts insertObject:newContact atIndex:indexPath.row]; [tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom];//注意这里没有使用reladData刷新 } }
运行效果:
只要实现-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath;代理方法当UITableView处于编辑状态时就能够排序。
#pragma mark 排序 //只要实现这个方法在编辑状态右侧就有排序图标 -(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{ KCContactGroup *sourceGroup =_contacts[sourceIndexPath.section]; KCContact *sourceContact=sourceGroup.contacts[sourceIndexPath.row]; KCContactGroup *destinationGroup =_contacts[destinationIndexPath.section]; [sourceGroup.contacts removeObject:sourceContact]; if(sourceGroup.contacts.count==0){ [_contacts removeObject:sourceGroup]; [tableView reloadData]; } [destinationGroup.contacts insertObject:sourceContact atIndex:destinationIndexPath.row]; }
运行效果:
最后给你们附上上面几种操做的完整代码:
// // KCContactViewController.m // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCContactViewController.h" #import "KCContact.h" #import "KCContactGroup.h" #define kContactToolbarHeight 44 @interface KCContactViewController ()<UITableViewDataSource,UITableViewDelegate,UIAlertViewDelegate>{ UITableView *_tableView; UIToolbar *_toolbar; NSMutableArray *_contacts;//联系人模型 NSIndexPath *_selectedIndexPath;//当前选中的组和行 BOOL _isInsert;//记录是点击了插入仍是删除按钮 } @end @implementation KCContactViewController - (void)viewDidLoad { [super viewDidLoad]; //初始化数据 [self initData]; //建立一个分组样式的UITableView _tableView=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; _tableView.contentInset=UIEdgeInsetsMake(kContactToolbarHeight, 0, 0, 0); [self.view addSubview:_tableView]; //添加工具栏 [self addToolbar]; //设置数据源,注意必须实现对应的UITableViewDataSource协议 _tableView.dataSource=self; //设置代理 _tableView.delegate=self; } #pragma mark 加载数据 -(void)initData{ _contacts=[[NSMutableArray alloc]init]; KCContact *contact1=[KCContact initWithFirstName:@"Cui" andLastName:@"Kenshin" andPhoneNumber:@"18500131234"]; KCContact *contact2=[KCContact initWithFirstName:@"Cui" andLastName:@"Tom" andPhoneNumber:@"18500131237"]; KCContactGroup *group1=[KCContactGroup initWithName:@"C" andDetail:@"With names beginning with C" andContacts:[NSMutableArray arrayWithObjects:contact1,contact2, nil]]; [_contacts addObject:group1]; KCContact *contact3=[KCContact initWithFirstName:@"Lee" andLastName:@"Terry" andPhoneNumber:@"18500131238"]; KCContact *contact4=[KCContact initWithFirstName:@"Lee" andLastName:@"Jack" andPhoneNumber:@"18500131239"]; KCContact *contact5=[KCContact initWithFirstName:@"Lee" andLastName:@"Rose" andPhoneNumber:@"18500131240"]; KCContactGroup *group2=[KCContactGroup initWithName:@"L" andDetail:@"With names beginning with L" andContacts:[NSMutableArray arrayWithObjects:contact3,contact4,contact5, nil]]; [_contacts addObject:group2]; KCContact *contact6=[KCContact initWithFirstName:@"Sun" andLastName:@"Kaoru" andPhoneNumber:@"18500131235"]; KCContact *contact7=[KCContact initWithFirstName:@"Sun" andLastName:@"Rosa" andPhoneNumber:@"18500131236"]; KCContactGroup *group3=[KCContactGroup initWithName:@"S" andDetail:@"With names beginning with S" andContacts:[NSMutableArray arrayWithObjects:contact6,contact7, nil]]; [_contacts addObject:group3]; KCContact *contact8=[KCContact initWithFirstName:@"Wang" andLastName:@"Stephone" andPhoneNumber:@"18500131241"]; KCContact *contact9=[KCContact initWithFirstName:@"Wang" andLastName:@"Lucy" andPhoneNumber:@"18500131242"]; KCContact *contact10=[KCContact initWithFirstName:@"Wang" andLastName:@"Lily" andPhoneNumber:@"18500131243"]; KCContact *contact11=[KCContact initWithFirstName:@"Wang" andLastName:@"Emily" andPhoneNumber:@"18500131244"]; KCContact *contact12=[KCContact initWithFirstName:@"Wang" andLastName:@"Andy" andPhoneNumber:@"18500131245"]; KCContactGroup *group4=[KCContactGroup initWithName:@"W" andDetail:@"With names beginning with W" andContacts:[NSMutableArray arrayWithObjects:contact8,contact9,contact10,contact11,contact12, nil]]; [_contacts addObject:group4]; KCContact *contact13=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joy" andPhoneNumber:@"18500131246"]; KCContact *contact14=[KCContact initWithFirstName:@"Zhang" andLastName:@"Vivan" andPhoneNumber:@"18500131247"]; KCContact *contact15=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joyse" andPhoneNumber:@"18500131248"]; KCContactGroup *group5=[KCContactGroup initWithName:@"Z" andDetail:@"With names beginning with Z" andContacts:[NSMutableArray arrayWithObjects:contact13,contact14,contact15, nil]]; [_contacts addObject:group5]; } #pragma mark 添加工具栏 -(void)addToolbar{ CGRect frame=self.view.frame; _toolbar=[[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, frame.size.width, kContactToolbarHeight)]; // _toolbar.backgroundColor=[UIColor colorWithHue:246/255.0 saturation:246/255.0 brightness:246/255.0 alpha:1]; [self.view addSubview:_toolbar]; UIBarButtonItem *removeButton=[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(remove)]; UIBarButtonItem *flexibleButton=[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; UIBarButtonItem *addButton=[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(add)]; NSArray *buttonArray=[NSArray arrayWithObjects:removeButton,flexibleButton,addButton, nil]; _toolbar.items=buttonArray; } #pragma mark 删除 -(void)remove{ //直接经过下面的方法设置编辑状态没有动画 //_tableView.editing=!_tableView.isEditing; _isInsert=false; [_tableView setEditing:!_tableView.isEditing animated:true]; } #pragma mark 添加 -(void)add{ _isInsert=true; [_tableView setEditing:!_tableView.isEditing animated:true]; } #pragma mark - 数据源方法 #pragma mark 返回分组数 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ return _contacts.count; } #pragma mark 返回每组行数 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ KCContactGroup *group1=_contacts[section]; return group1.contacts.count; } #pragma mark返回每行的单元格 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ //NSIndexPath是一个对象,记录了组和行信息 KCContactGroup *group=_contacts[indexPath.section]; KCContact *contact=group.contacts[indexPath.row]; static NSString *cellIdentifier=@"UITableViewCellIdentifierKey1"; //首先根据标识去缓存池取 UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier]; //若是缓存池没有取到则从新建立并放到缓存池中 if(!cell){ cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier]; } cell.textLabel.text=[contact getName]; cell.detailTextLabel.text=contact.phoneNumber; return cell; } #pragma mark - 代理方法 #pragma mark 设置分组标题 -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ KCContactGroup *group=_contacts[section]; return group.name; } #pragma mark 编辑操做(删除或添加) //实现了此方法向左滑动就会显示删除(或添加)图标 -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{ KCContactGroup *group =_contacts[indexPath.section]; KCContact *contact=group.contacts[indexPath.row]; if (editingStyle==UITableViewCellEditingStyleDelete) { [group.contacts removeObject:contact]; //考虑到性能这里不建议使用reloadData //[tableView reloadData]; //使用下面的方法既能够局部刷新又有动画效果 [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom]; //若是当前组中没有数据则移除组刷新整个表格 if (group.contacts.count==0) { [_contacts removeObject:group]; [tableView reloadData]; } }else if(editingStyle==UITableViewCellEditingStyleInsert){ KCContact *newContact=[[KCContact alloc]init]; newContact.firstName=@"first"; newContact.lastName=@"last"; newContact.phoneNumber=@"12345678901"; [group.contacts insertObject:newContact atIndex:indexPath.row]; [tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom];//注意这里没有使用reladData刷新 } } #pragma mark 排序 //只要实现这个方法在编辑状态右侧就有排序图标 -(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{ KCContactGroup *sourceGroup =_contacts[sourceIndexPath.section]; KCContact *sourceContact=sourceGroup.contacts[sourceIndexPath.row]; KCContactGroup *destinationGroup =_contacts[destinationIndexPath.section]; [sourceGroup.contacts removeObject:sourceContact]; [destinationGroup.contacts insertObject:sourceContact atIndex:destinationIndexPath.row]; if(sourceGroup.contacts.count==0){ [_contacts removeObject:sourceGroup]; [tableView reloadData]; } } #pragma mark 取得当前操做状态,根据不一样的状态左侧出现不一样的操做按钮 -(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{ if (_isInsert) { return UITableViewCellEditingStyleInsert; } return UITableViewCellEditingStyleDelete; } #pragma mark 重写状态样式方法 -(UIStatusBarStyle)preferredStatusBarStyle{ return UIStatusBarStyleLightContent; } @end
经过前面的演示这里简单总结一些UITableView的刷新方法:
- (void)reloadData;刷新整个表格。
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation NS_AVAILABLE_IOS(3_0);刷新指定的分组和行。
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation NS_AVAILABLE_IOS(3_0);刷新指定的分组。
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;删除时刷新指定的行数据。
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;添加时刷新指定的行数据。
不少时候一个UIViewController中只有一个UITableView,所以苹果官方为了方便你们开发直接提供了一个UITableViewController,这个控制器 UITableViewController实现了UITableView数据源和代理协议,内部定义了一个tableView属性供外部访问,同时自动铺满整个屏幕、自动伸缩以方便咱们的开发。固然UITableViewController也并非简单的帮咱们定义完UITableView而且设置了数据源、代理而已,它还有其余强大的功能,例如刷新控件、滚动过程当中固定分组标题等。
有时候一个表格中的数据特别多,检索起来就显得麻烦,这个时候能够实现一个搜索功能帮助用户查找数据,其实搜索的原理很简单:修改模型、刷新表格。下面使用UITableViewController简单演示一下这个功能:
// // KCContactTableViewController.m // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCContactTableViewController.h" #import "KCContact.h" #import "KCContactGroup.h" #define kSearchbarHeight 44 @interface KCContactTableViewController ()<UISearchBarDelegate>{ UITableView *_tableView; UISearchBar *_searchBar; //UISearchDisplayController *_searchDisplayController; NSMutableArray *_contacts;//联系人模型 NSMutableArray *_searchContacts;//符合条件的搜索联系人 BOOL _isSearching; } @end @implementation KCContactTableViewController - (void)viewDidLoad { [super viewDidLoad]; //初始化数据 [self initData]; //添加搜索框 [self addSearchBar]; } #pragma mark - 数据源方法 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { if (_isSearching) { return 1; } return _contacts.count;; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (_isSearching) { return _searchContacts.count; } KCContactGroup *group1=_contacts[section]; return group1.contacts.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { KCContact *contact=nil; if (_isSearching) { contact=_searchContacts[indexPath.row]; }else{ KCContactGroup *group=_contacts[indexPath.section]; contact=group.contacts[indexPath.row]; } static NSString *cellIdentifier=@"UITableViewCellIdentifierKey1"; //首先根据标识去缓存池取 UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier]; //若是缓存池没有取到则从新建立并放到缓存池中 if(!cell){ cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier]; } cell.textLabel.text=[contact getName]; cell.detailTextLabel.text=contact.phoneNumber; return cell; } #pragma mark - 代理方法 #pragma mark 设置分组标题 -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ KCContactGroup *group=_contacts[section]; return group.name; } #pragma mark - 搜索框代理 #pragma mark 取消搜索 -(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar{ _isSearching=NO; _searchBar.text=@""; [self.tableView reloadData]; } #pragma mark 输入搜索关键字 -(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{ if([_searchBar.text isEqual:@""]){ _isSearching=NO; [self.tableView reloadData]; return; } [self searchDataWithKeyWord:_searchBar.text]; } #pragma mark 点击虚拟键盘上的搜索时 -(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{ [self searchDataWithKeyWord:_searchBar.text]; [_searchBar resignFirstResponder];//放弃第一响应者对象,关闭虚拟键盘 } #pragma mark 重写状态样式方法 -(UIStatusBarStyle)preferredStatusBarStyle{ return UIStatusBarStyleLightContent; } #pragma mark 加载数据 -(void)initData{ _contacts=[[NSMutableArray alloc]init]; KCContact *contact1=[KCContact initWithFirstName:@"Cui" andLastName:@"Kenshin" andPhoneNumber:@"18500131234"]; KCContact *contact2=[KCContact initWithFirstName:@"Cui" andLastName:@"Tom" andPhoneNumber:@"18500131237"]; KCContactGroup *group1=[KCContactGroup initWithName:@"C" andDetail:@"With names beginning with C" andContacts:[NSMutableArray arrayWithObjects:contact1,contact2, nil]]; [_contacts addObject:group1]; KCContact *contact3=[KCContact initWithFirstName:@"Lee" andLastName:@"Terry" andPhoneNumber:@"18500131238"]; KCContact *contact4=[KCContact initWithFirstName:@"Lee" andLastName:@"Jack" andPhoneNumber:@"18500131239"]; KCContact *contact5=[KCContact initWithFirstName:@"Lee" andLastName:@"Rose" andPhoneNumber:@"18500131240"]; KCContactGroup *group2=[KCContactGroup initWithName:@"L" andDetail:@"With names beginning with L" andContacts:[NSMutableArray arrayWithObjects:contact3,contact4,contact5, nil]]; [_contacts addObject:group2]; KCContact *contact6=[KCContact initWithFirstName:@"Sun" andLastName:@"Kaoru" andPhoneNumber:@"18500131235"]; KCContact *contact7=[KCContact initWithFirstName:@"Sun" andLastName:@"Rosa" andPhoneNumber:@"18500131236"]; KCContactGroup *group3=[KCContactGroup initWithName:@"S" andDetail:@"With names beginning with S" andContacts:[NSMutableArray arrayWithObjects:contact6,contact7, nil]]; [_contacts addObject:group3]; KCContact *contact8=[KCContact initWithFirstName:@"Wang" andLastName:@"Stephone" andPhoneNumber:@"18500131241"]; KCContact *contact9=[KCContact initWithFirstName:@"Wang" andLastName:@"Lucy" andPhoneNumber:@"18500131242"]; KCContact *contact10=[KCContact initWithFirstName:@"Wang" andLastName:@"Lily" andPhoneNumber:@"18500131243"]; KCContact *contact11=[KCContact initWithFirstName:@"Wang" andLastName:@"Emily" andPhoneNumber:@"18500131244"]; KCContact *contact12=[KCContact initWithFirstName:@"Wang" andLastName:@"Andy" andPhoneNumber:@"18500131245"]; KCContactGroup *group4=[KCContactGroup initWithName:@"W" andDetail:@"With names beginning with W" andContacts:[NSMutableArray arrayWithObjects:contact8,contact9,contact10,contact11,contact12, nil]]; [_contacts addObject:group4]; KCContact *contact13=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joy" andPhoneNumber:@"18500131246"]; KCContact *contact14=[KCContact initWithFirstName:@"Zhang" andLastName:@"Vivan" andPhoneNumber:@"18500131247"]; KCContact *contact15=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joyse" andPhoneNumber:@"18500131248"]; KCContactGroup *group5=[KCContactGroup initWithName:@"Z" andDetail:@"With names beginning with Z" andContacts:[NSMutableArray arrayWithObjects:contact13,contact14,contact15, nil]]; [_contacts addObject:group5]; } #pragma mark 搜索造成新数据 -(void)searchDataWithKeyWord:(NSString *)keyWord{ _isSearching=YES; _searchContacts=[NSMutableArray array]; [_contacts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { KCContactGroup *group=obj; [group.contacts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { KCContact *contact=obj; if ([contact.firstName.uppercaseString containsString:keyWord.uppercaseString]||[contact.lastName.uppercaseString containsString:keyWord.uppercaseString]||[contact.phoneNumber containsString:keyWord]) { [_searchContacts addObject:contact]; } }]; }]; //刷新表格 [self.tableView reloadData]; } #pragma mark 添加搜索栏 -(void)addSearchBar{ CGRect searchBarRect=CGRectMake(0, 0, self.view.frame.size.width, kSearchbarHeight); _searchBar=[[UISearchBar alloc]initWithFrame:searchBarRect]; _searchBar.placeholder=@"Please input key word..."; //_searchBar.keyboardType=UIKeyboardTypeAlphabet;//键盘类型 //_searchBar.autocorrectionType=UITextAutocorrectionTypeNo;//自动纠错类型 //_searchBar.autocapitalizationType=UITextAutocapitalizationTypeNone;//哪一次shitf被自动按下 _searchBar.showsCancelButton=YES;//显示取消按钮 //添加搜索框到页眉位置 _searchBar.delegate=self; self.tableView.tableHeaderView=_searchBar; } @end
运行效果:
在上面的搜索中除了使用一个_contacts变量去保存联系人数据还专门定义了一个_searchContact变量用于保存搜索的结果。在输入搜索关键字时咱们刷新了表格,此时会调用表格的数据源方法,在这个方法中咱们根据定义的搜索状态去决定显示原始数据仍是搜索结果。
咱们发现每次搜索完后都须要手动刷新表格来显示搜索结果,并且当没有搜索关键字的时候还须要将当前的tableView从新设置为初始状态。也就是这个过程当中咱们要用一个tableView显示两种状态的不一样数据,天然会提升程序逻辑复杂度。为了简化这个过程,咱们可使用UISearchDisplayController,UISearchDisplayController内部也有一个UITableView类型的对象searchResultsTableView,若是咱们设置它的数据源代理为当前控制器,那么它彻底能够像UITableView同样加载数据。同时它自己也有搜索监听的方法,咱们没必要在监听UISearchBar输入内容,直接使用它的方法便可自动刷新其内部表格。为了和前面的方法对比在下面的代码中没有直接删除原来的方式而是注释了对应代码你们能够对照学习:
// // KCContactTableViewController.m // UITableView // // Created by Kenshin Cui on 14-3-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCContactTableViewControllerWithUISearchDisplayController.h" #import "KCContact.h" #import "KCContactGroup.h" #define kSearchbarHeight 44 @interface KCContactTableViewControllerWithUISearchDisplayController ()<UISearchBarDelegate,UISearchDisplayDelegate>{ UITableView *_tableView; UISearchBar *_searchBar; UISearchDisplayController *_searchDisplayController; NSMutableArray *_contacts;//联系人模型 NSMutableArray *_searchContacts;//符合条件的搜索联系人 //BOOL _isSearching; } @end @implementation KCContactTableViewControllerWithUISearchDisplayController - (void)viewDidLoad { [super viewDidLoad]; //初始化数据 [self initData]; //添加搜索框 [self addSearchBar]; } #pragma mark - 数据源方法 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // if (_isSearching) { // return 1; // } //若是当前是UISearchDisplayController内部的tableView则不分组 if (tableView==self.searchDisplayController.searchResultsTableView) { return 1; } return _contacts.count;; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // if (_isSearching) { // return _searchContacts.count; // } //若是当前是UISearchDisplayController内部的tableView则使用搜索数据 if (tableView==self.searchDisplayController.searchResultsTableView) { return _searchContacts.count; } KCContactGroup *group1=_contacts[section]; return group1.contacts.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { KCContact *contact=nil; // if (_isSearching) { // contact=_searchContacts[indexPath.row]; // }else{ // KCContactGroup *group=_contacts[indexPath.section]; // contact=group.contacts[indexPath.row]; // } //若是当前是UISearchDisplayController内部的tableView则使用搜索数据 if (tableView==self.searchDisplayController.searchResultsTableView) { contact=_searchContacts[indexPath.row]; }else{ KCContactGroup *group=_contacts[indexPath.section]; contact=group.contacts[indexPath.row]; } static NSString *cellIdentifier=@"UITableViewCellIdentifierKey1"; //首先根据标识去缓存池取 UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier]; //若是缓存池没有取到则从新建立并放到缓存池中 if(!cell){ cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier]; } cell.textLabel.text=[contact getName]; cell.detailTextLabel.text=contact.phoneNumber; return cell; } #pragma mark - 代理方法 #pragma mark 设置分组标题 -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ if (tableView==self.searchDisplayController.searchResultsTableView) { return @"搜索结果"; } KCContactGroup *group=_contacts[section]; return group.name; } #pragma mark 选中以前 -(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath{ [_searchBar resignFirstResponder];//退出键盘 return indexPath; } #pragma mark - 搜索框代理 //#pragma mark 取消搜索 //-(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar{ // //_isSearching=NO; // _searchBar.text=@""; // //[self.tableView reloadData]; // [_searchBar resignFirstResponder]; //} // //#pragma mark 输入搜索关键字 //-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{ // if([_searchBar.text isEqual:@""]){ // //_isSearching=NO; // //[self.tableView reloadData]; // return; // } // [self searchDataWithKeyWord:_searchBar.text]; //} //#pragma mark 点击虚拟键盘上的搜索时 //-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{ // // [self searchDataWithKeyWord:_searchBar.text]; // // [_searchBar resignFirstResponder];//放弃第一响应者对象,关闭虚拟键盘 //} #pragma mark - UISearchDisplayController代理方法 -(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString{ [self searchDataWithKeyWord:searchString]; return YES; } #pragma mark 重写状态样式方法 -(UIStatusBarStyle)preferredStatusBarStyle{ return UIStatusBarStyleLightContent; } #pragma mark 加载数据 -(void)initData{ _contacts=[[NSMutableArray alloc]init]; KCContact *contact1=[KCContact initWithFirstName:@"Cui" andLastName:@"Kenshin" andPhoneNumber:@"18500131234"]; KCContact *contact2=[KCContact initWithFirstName:@"Cui" andLastName:@"Tom" andPhoneNumber:@"18500131237"]; KCContactGroup *group1=[KCContactGroup initWithName:@"C" andDetail:@"With names beginning with C" andContacts:[NSMutableArray arrayWithObjects:contact1,contact2, nil]]; [_contacts addObject:group1]; KCContact *contact3=[KCContact initWithFirstName:@"Lee" andLastName:@"Terry" andPhoneNumber:@"18500131238"]; KCContact *contact4=[KCContact initWithFirstName:@"Lee" andLastName:@"Jack" andPhoneNumber:@"18500131239"]; KCContact *contact5=[KCContact initWithFirstName:@"Lee" andLastName:@"Rose" andPhoneNumber:@"18500131240"]; KCContactGroup *group2=[KCContactGroup initWithName:@"L" andDetail:@"With names beginning with L" andContacts:[NSMutableArray arrayWithObjects:contact3,contact4,contact5, nil]]; [_contacts addObject:group2]; KCContact *contact6=[KCContact initWithFirstName:@"Sun" andLastName:@"Kaoru" andPhoneNumber:@"18500131235"]; KCContact *contact7=[KCContact initWithFirstName:@"Sun" andLastName:@"Rosa" andPhoneNumber:@"18500131236"]; KCContactGroup *group3=[KCContactGroup initWithName:@"S" andDetail:@"With names beginning with S" andContacts:[NSMutableArray arrayWithObjects:contact6,contact7, nil]]; [_contacts addObject:group3]; KCContact *contact8=[KCContact initWithFirstName:@"Wang" andLastName:@"Stephone" andPhoneNumber:@"18500131241"]; KCContact *contact9=[KCContact initWithFirstName:@"Wang" andLastName:@"Lucy" andPhoneNumber:@"18500131242"]; KCContact *contact10=[KCContact initWithFirstName:@"Wang" andLastName:@"Lily" andPhoneNumber:@"18500131243"]; KCContact *contact11=[KCContact initWithFirstName:@"Wang" andLastName:@"Emily" andPhoneNumber:@"18500131244"]; KCContact *contact12=[KCContact initWithFirstName:@"Wang" andLastName:@"Andy" andPhoneNumber:@"18500131245"]; KCContactGroup *group4=[KCContactGroup initWithName:@"W" andDetail:@"With names beginning with W" andContacts:[NSMutableArray arrayWithObjects:contact8,contact9,contact10,contact11,contact12, nil]]; [_contacts addObject:group4]; KCContact *contact13=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joy" andPhoneNumber:@"18500131246"]; KCContact *contact14=[KCContact initWithFirstName:@"Zhang" andLastName:@"Vivan" andPhoneNumber:@"18500131247"]; KCContact *contact15=[KCContact initWithFirstName:@"Zhang" andLastName:@"Joyse" andPhoneNumber:@"18500131248"]; KCContactGroup *group5=[KCContactGroup initWithName:@"Z" andDetail:@"With names beginning with Z" andContacts:[NSMutableArray arrayWithObjects:contact13,contact14,contact15, nil]]; [_contacts addObject:group5]; } #pragma mark 搜索造成新数据 -(void)searchDataWithKeyWord:(NSString *)keyWord{ //_isSearching=YES; _searchContacts=[NSMutableArray array]; [_contacts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { KCContactGroup *group=obj; [group.contacts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { KCContact *contact=obj; if ([contact.firstName.uppercaseString containsString:keyWord.uppercaseString]||[contact.lastName.uppercaseString containsString:keyWord.uppercaseString]||[contact.phoneNumber containsString:keyWord]) { [_searchContacts addObject:contact]; } }]; }]; //刷新表格 //[self.tableView reloadData]; } #pragma mark 添加搜索栏 -(void)addSearchBar{ _searchBar=[[UISearchBar alloc]init]; [_searchBar sizeToFit];//大小自适应容器 _searchBar.placeholder=@"Please input key word..."; _searchBar.autocapitalizationType=UITextAutocapitalizationTypeNone; _searchBar.showsCancelButton=YES;//显示取消按钮 //添加搜索框到页眉位置 _searchBar.delegate=self; self.tableView.tableHeaderView=_searchBar; _searchDisplayController=[[UISearchDisplayController alloc]initWithSearchBar:_searchBar contentsController:self]; _searchDisplayController.delegate=self; _searchDisplayController.searchResultsDataSource=self; _searchDisplayController.searchResultsDelegate=self; [_searchDisplayController setActive:NO animated:YES]; } @end
运行效果:
注意若是使用Storyboard或xib方式建立上述代码则无需定义UISearchDisplayController成员变量,由于每一个UIViewController中已经有一个searchDisplayController对象。
经过UITableView的学习相信你们对于iOS的MVC已经有一个大体的了解,这里简单的分析一下iOS中MVC模式的设计方式。在iOS中多数数据源视图控件(View)都有一个dataSource属性用于和控制器(Controller)交互,而数据来源咱们通常会以数据模型(Model)的形式进行定义,View不直接和模型交互,而是经过Controller间接读取数据。
就拿前面的联系人应用举例,UITableView做为视图(View)并不能直接访问模型Contact,它要显示联系人信息只能经过控制器(Controller)来提供数据源方法。一样的控制器自己就拥有视图控件,能够操做视图,也就是说视图和控制器之间能够互相访问。而模型既不能访问视图也不能访问控制器。具体依赖关系以下图:
![]() |
本做品采用知识共享署名 2.5 中国大陆许可协议进行许可,欢迎转载,演绎或用于商业目的。但转载请注明来自崔江涛(KenshinCui),并包含相关连接。 |